Створення саморобних аксесуарів для Dendy

Спочатку я і не думав писати статтю на цю тему, але схоже, що це вже частина цілого циклу статей на Денді-тематику. І так, на цей раз мова в першу чергу саме про вітчизняну Денді, а не про оригінальні консолі — 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 (enter), $4017.0-4 (enter), $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 [4:0] counter1;
reg [4:0] counter2;

завжди @ (posedge strobe_in, posedge clock1_in)
begin
if (strobe_in)
counter1 <= 1;
else if (counter1 < 31)
counter1 <= counter1 + 1;
end

завжди @ (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[0] = strobe_in;
assign strobe_out[1] = strobe_in;
assign strobe_out[2] = strobe_in;
assign strobe_out[3] = strobe_in;

// Смикаємо clock у кожного геймпада в залежності від того, в який раз читає дані консоль
clock_out[0] <= (counter1 <= 8) ? clock1_in : 1;
clock_out[1] <= (counter2 <= 8) ? clock2_in : 1;
clock_out[2] <= (counter1 > 8 && counter1 <= 16) ? clock1_in : 1;
clock_out[3] <= (counter2 > 8 && counter2 <= 16) ? clock2_in : 1;

if (counter1 <= 8)
// Перший контролер
joy1_data_out[0] <= joy_data[0];
else if (counter1 <= 16)
// Третій контролер
joy1_data_out[0] <= joy_data[2];
// Сигнатура
else if (counter1 == 20)
joy1_data_out[0] <= 0;
else
joy1_data_out[0] <= 1;

// Другий контролер
if (counter2 <= 8)
joy2_data_out[0] <= joy_data[1];
// Четвертий контролер
else if (counter2 <= 16)
joy2_data_out[0] <= joy_data[3];
// Сигнатура
else if (counter2 == 19)
joy2_data_out[0] <= 0;
else
joy2_data_out[0] <= 1;

// Невикористовувані висновки залишаємо в высокоимпедансном стані, вони підтягуються до VCC всередині самої консолі
joy1_data_out[1] <= 1'bZ;
joy2_data_out[4:1] <= 4'bZZZZ;
end


В режимі японського ж развитвителя на чотирьох потрібно просто з'єднати входи з виходами напряму:
always @ (*)
begin
clock_out[0] <= clock1_in;
clock_out[1] <= clock2_in;
clock_out[2] <= clock1_in;
clock_out[3] <= clock2_in;

joy1_data_out[0] <= joy_data[0];
joy2_data_out[0] <= joy_data[1];
joy1_data_out[1] <= joy_data[2];
joy2_data_out[1] <= joy_data[3];

// Невикористовувані висновки залишаємо в высокоимпедансном стані, вони підтягуються до VCC всередині самої консолі
joy2_data_out[4:2] <= 3'bZZZ; 
end


Найскладнішим виявилося зробити можливість міняти місцями кнопки A і B, адже вони зчитуються послідовно, тобто потрібно заздалегідь знати значення B, коли консоль запитує A, але воно видається тільки після A. Спочатку я думав, як-то прискорено зчитувати дані з контролера, використовуючи якийсь зовнішній тактовий генератор, але в підсумку вирішив просто брати значення від попереднього зчитування. Це дає затримку, але вона абсолютно непомітна. Тим більше гри зазвичай читають стан кнопок по кілька разів поспіль.

Само собою, всі ці режими і настройки треба якось ставити. Для цього я визначив 12-бітний регістр ctrl, дані в який записуються через послідовне з'єднання, з додатковим бітом для перевірки парності:
reg [11:0] control;
reg control_parity;
reg [11:0] control_receiver;
reg [3:0] control_counter;

завжди @ (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_counter] = control_data;
control_parity = control_parity ^ control_data;
end;
if (control_counter < 12)
control_counter = control_counter + 1;
end
end

завжди @ (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<<b))
{
set_bit(CTRL_PORT, CTRL_DATA_PIN); // Data
parity ^= 1;
} else {
unset_bit(CTRL_PORT, CTRL_DATA_PIN); // Data
}
unset_bit(CTRL_PORT, CTRL_CLOCK_PIN); // Clock
_delay_us(10);
set_bit(CTRL_PORT, CTRL_CLOCK_PIN); // Clock
_delay_us(10);
}
if (parity)
{
set_bit(CTRL_PORT, CTRL_DATA_PIN); // Data
} else {
unset_bit(CTRL_PORT, CTRL_DATA_PIN); // Data
}
unset_bit(CTRL_PORT, CTRL_CLOCK_PIN); // Clock
_delay_us(10);
set_bit(CTRL_PORT, CTRL_CLOCK_PIN); // Clock
_delay_us(10);
}


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

Я все отладил, переконався в працездатності і вже почав упихивать компоненти в корпус…



Але перед закриттям кришки вирішив перевірити на оригінальному Famicom, адже з ним пристрій теж буде використовуватися. На жаль, режими, де потрібно було рахувати імпульси clock, працювали неправильно. За допомогою логічного аналізатора з'ясувалося, що з лінії даних йдуть наведення на лінію clock:



Це перешкода тривалістю всього в кілька десятків наносекунд все псує. Я вирішив подивитися своїм простеньким осцилографом, що ж відбувається на лінії clock у Денді:



А ось що там же у Фамикома:



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



На неї відразу стало все наочно видно, а заодно можна було протестувати відразу всі режими, не змінюючи гри. ROM можна скачати тут.

У підсумку проблема була вирішена підтяжкою ліній clock до VCC через резистор в 1кОм, конденсатором між clock і землею в 22нФ і резисторами на 200 Ом в розрив всіх ліній даних. На жаль, довелося труїти нову плату (не фотографував), але зате після цього відразу ж все запрацювало.

Підсумковий вигляд пристрою:



За часів СРСР я міг би бути хорошим промдизайнером.

Багато хто напевно захочуть побачити відео, але на даний момент подарунок вже в руках нашої пошти, а я зняв лише невелику відеоінструкцію для кінцевого користувача. Переглянути її можна тут: www.youtube.com/watch?v=39beci7nE8w

І якщо вас зацікавила тематика роботи різних ігрових контролерів і створення саморобних, ми як раз на цю тему зняли другу серію нашого шоу «Поки всі грають», де багато дуже просто і наочно пояснюється для тих, хто зовсім не в темі:



Інформація по архітектурі цих консолей і аксесуарів бралася з сайту wiki.nesdev.com
Повний код для ПЛІС на Verilog: pastebin.com/nt39ZGvH

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.