IO-подсистема

Устройства ввода-вывода для NES, Famicom и Dendy: реальные и виртуальные контроллеры и архитектура их привязки к материнской плате

Данный раздел содержит реализацию логики различных устройств ввода/вывода для NES/Famicom/Dendy, портов расширения и сопроводительных чипов.

TBD: порты расширения пока не реализованы и не рассматриваются.

Обзор

Как может показаться - что может быть сложного в обычном контроллере? Однако...

И как всё это красиво обобщить? :-)

Архитектура IO-подсистемы

Настройка IO-устройств

Со стороны эмулятора настройка сводится к привязке IOState к какому-то API для работы с устройствами ввода на стороне ПК. Затем ассоциативная связь используется для установки SetState подключённого устройства.

Эмулятор содержит пул зарегистрированных устройств с указанием DeviceID (устройства добавляет и удаляет пользователь).

Далее каждое устройство можно настроить (грубо говоря, сделать привязку IOState к DirectInput/XInput или ещё какому-то API). У каждого типа устройств свой диалог привязки кнопок/прочих актуаторов.

После настройки устройство можно подключить или отключить к указанной модели материнской платы (Attach / Detach). После запуска эмуляции для IO-подсистемы будут созданы экземпляры подключённых устройств, подключены к портам, и далее эмулятор может вызывать со своей стороны SetState, чтобы нативная реализация преобразовывала IOState в конкретные сигналы для портов ввода/вывода.

Опять же, описание весьма абстрактное — по ходу работ оно кристаллизуется в конкретные классы/методы.

Абстрактное устройство

DeviceID: 0x00000000

Не содержит никаких IOState.

Famicom Controller (Port1)

Контроллер Famicom (Port1)

DeviceID: 0x00000001

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4Select0/1
5Start0/1
6B0/1
7A0/1

Контроллеры Famicom, хотя внешне и несъёмные, но внутри корпуса их всё же можно отсоединить, поэтому для унификации кода будем считать их съёмными.

Famicom Controller (Port2)

Контроллер Famicom (Port2)

DeviceID: 0x00000002

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4B0/1
5A0/1
6Volume0...255
7MicLevel0...255

Значения громкости и уровня микрофона пока в диапазоне UInt8 — посмотрим, насколько хороша эта модель в процессе.

NES Controller (Port1/2)

Контроллер NES (Port1/2)

DeviceID: 0x00000003

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4Select0/1
5Start0/1
6B0/1
7A0/1

Dendy Turbo Controller (Port1/2)

Контроллер Dendy Turbo (Port1/2)

DeviceID: 0x00000004

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4Select0/1
5Start0/1
6TurboB0/1
7TurboA0/1
8B0/1
9A0/1

Virtual Famicom Controller (Port1)

Виртуальный контроллер Famicom (Port1)

DeviceID: 0x00010001

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4Select0/1
5Start0/1
6B0/1
7A0/1

Virtual Famicom Controller (Port2)

Виртуальный контроллер Famicom (Port2)

DeviceID: 0x00010002

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4B0/1
5A0/1
6Volume0...255
7MicLevel0...255

Virtual NES Controller (Port1/2)

Виртуальный контроллер NES (Port1/2)

DeviceID: 0x00010003

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4Select0/1
5Start0/1
6B0/1
7A0/1

Virtual Dendy Turbo Controller (Port1/2)

Виртуальный контроллер Dendy Turbo (Port1/2)

DeviceID: 0x00010004

IOStateActuatorValues
0Up0/1
1Down0/1
2Left0/1
3Right0/1
4Select0/1
5Start0/1
6TurboB0/1
7TurboA0/1
8B0/1
9A0/1