BreaksCore — нативная часть эмулятора: эмулятор NES/Famicom/Dendy на уровне вентилей, на котором построены оба фронтенда. Поверх нативного ядра расположены два приложения: управляемое приложение WinForms (Breaknes) и, для тех, кто любит минимализм, сборка Breaknes на SDL2 (BreaknesSDL).
BreaksCore
Нативная часть эмулятора.
Абстрактная плата
Абстрактная материнская плата используется как базовый класс для всех остальных вариантов NES/Famicom/Dendy.
Для создания экземпляра материнской платы используется Board Factory:
- Родительское приложение сообщает фабрике, какую плату создать (по имени).
- Создаётся класс соответствующей материнской платы (
NESBoard.cpp,FamicomBoard.cpp).
Пока это немного абстрактно, но позже мы свяжем классы между собой.
Платы
Список поддерживаемых плат.
Famicom
Планируется симулировать плату Generic Famicom следующим образом:
NES
Планируется симулировать плату Generic NES следующим образом:
Dendy/Subor/Phantom/Whatever
TBD.
Bogus Board
BogusBoard — материнская плата по умолчанию, если BreaksCore не распознал тип платы при создании экземпляров или были переданы другие ошибочные параметры.
Эта система содержит 64 Кбайт ОЗУ, процессорное ядро 6502 и больше ничего.
Иными словами, это минимальная вычислительная система, позволяющая проверить, тикает ли что-нибудь в эмуляторе.
Плата PPUPlayer
Специальная debug-плата для PPUPlayer.
Плата содержит PPU, окружающую логику (Address Latch, VRAM) и частичную поддержку картриджного разъёма (используются только выводы PPU/CHR).
Плата APUPlayer
Ещё одна debug-плата для «проигрывания» дампа регистров APU.
Она примерно такая же, как PPUPlayer, но проще: APU не требует картриджного разъёма и дополнительных привязок, связанных с ним.
Сэмплы DPCM загружаются в расширенную память объёмом до 64 Кбайт (WRAM).
Debug Hub
Отладочная инфраструктура Breaknes.
Предупреждение: текущая реализация основана на строковых ассоциативных списках и не может считаться сверхбыстрой.
Поскольку Breaknes — эмулятор на уровне вентилей, главный фокус отладки — наблюдение за различными сигналами, шинами и другими внутренними состояниями эмулируемой системы.
Сущности отладки разделены на следующие категории:
DebugInfo: получение/установка внутреннего состоянияMemLayout: чтение/запись дампов памяти
DebugInfo
Со стороны GUI DebugInfo отображается как PropertyGrid. PropertyGrid заполняется динамически на основе записей отладочной информации, возвращаемых нативным ядром Breaknes.
Типы отладочной информации (DebugInfoType):
DebugInfoType_Core: получить внутреннее состояние M6502CoreDebugInfoType_CoreRegs: получить состояние регистров 6502DebugInfoType_APU: получить внутреннее состояние компонентов APUDebugInfoType_APURegs: получить состояние регистров APUDebugInfoType_PPU: получить внутреннее состояние компонентов PPUDebugInfoType_PPURegs: получить состояние регистров PPUDebugInfoType_Board: получить внутреннее состояние компонентов материнской платыDebugInfoType_Cart: получить внутреннее состояние картриджа и компонентов маппера
Методы:
GetDebugInfoEntryCount: получить количество debug-записей указанного типаGetDebugInfo: получить все записи указанного типа. Управляемое приложение должно сначала узнать число записей и выделить массив для их получения.GetDebugInfoByName: получить одну запись DebugInfo с указанным именемSetDebugInfoByName: установить одну запись DebugInfo с указанным именем
MemLayout
Различные области памяти внутри эмулируемой системы описываются структурой MemDesrciptor.
Методы:
GetMemLayout: получить количество дескрипторов памяти, зарегистрированных в DebugHubGetMemDescriptor: получить информацию о блоке памятиDumpMem: получить весь блок памяти целиком. Мы эмулируем здесь NES, поэтому размеры дампов будут небольшими, и дробить дамп на части не имеет смысла.WriteMem: установить весь блок памяти
Управляемое приложение (Breaknes)
Эмулятор NES/Famicom/Dendy на уровне вентилей.
Два телевизора (issue #515)
Эмулятор поддерживает до 2 виртуальных телевизоров. Видеосигнал каждого PPU материнской платы привязан к телевизору; привязка настраивается в BoardDescription.json:
ppus— ревизии PPU платы (массив, до 2). Устаревшее одиночное полеppuпо-прежнему поддерживается.tvs— привязка телевизоров:tvs[i]— это индекс PPU, чей сигнал отображается на i-м телевизоре. Привязка по умолчанию:TV[i]показываетPPU[i]. Для отладки один PPU можно привязать сразу к обоим телевизорам ("tvs": [0, 0]).tv_layout— физическое расположение телевизоров в окне:"horizontal"(рядом) или"vertical"(один над другим).
Выберите в настройках debug-плату («NES Debug: one PPU on two TVs (horizontal)» или «NES Debug: one PPU on two TVs (vertical)»), чтобы увидеть окно с двумя телевизорами в действии.
Сборка
Используйте VS2022.
Чтобы нативная и управляемая части генерировались в одну папку Build, нужно воспользоваться этим:
SDL2-порт (BreaknesSDL)
Для тех, кто любит минимализм — сборка Breaknes на SDL2.
Реализация не отличается от управляемого приложения: используется та же нативная часть BreaksCore. Ревизии материнской платы и чипов берутся из BoardDescription.json (рядом с исполняемым файлом — файл общий с управляемым приложением): SDL-сборка ищет плату по имени, конфиг обязателен (встроенного запасного варианта нет).
Выбор платы
По умолчанию используется запись NES (NES-CPU-07, 1987) из BoardDescription.json. Любую другую плату из того же файла можно выбрать в командной строке:
breaknes --board "NES-101 (NESN-CPU-JIO-01, 1994-1995)" game.nes
Два телевизора (issue #515)
SDL-сборка отображает видеосигнал каждого подключённого телевизора в одном окне. Поддерживается до 2 телевизоров; физическое расположение управляется свойством tv_layout платы в BoardDescription.json:
"tv_layout": "horizontal"— телевизоры располагаются рядом"tv_layout": "vertical"— телевизоры располагаются один над другим
Привязка PPU к телевизорам описывается массивом tvs (tvs[i] = индекс PPU, показываемого на i-м телевизоре). Привязка по умолчанию: TV[i] показывает PPU[i]; для отладки один PPU можно привязать сразу к обоим телевизорам ("tvs": [0, 0]).
Подсистема ввода-вывода
Подсистема ввода-вывода встроена в SDL-сборку (issue #516). В качестве источника событий ввода используется SDL2 Input:
- Клавиатура — имена клавиш SDL (
Up,Down,Left,Right,A,S,Z,X,Return,Space, ...) - Геймпады (если доступны) —
Controller<slot>_ButtonA,Controller<slot>_DPadUp,Controller<slot>_ButtonStart, ...; аналоговые стики сопоставляются с цифровыми событиямиController<slot>_AxisLeftX-/_AxisLeftX+и т.д.; аналоговые триггеры — сController<slot>_TriggerLeft/_TriggerRight.
Устройства (контроллеры Famicom/NES/Dendy, виртуальные контроллеры), их привязки IOState к событиям ввода и статус Attach/Detach к портам материнской платы настраиваются интерактивно:
breaknes --ioconfig [config_path]
Логика конфигуратора основана на диалоге настроек IO управляемого приложения (Breaknes/FormIOConfig.cs): пул устройств (добавление/удаление), привязки (IOState <-> событие ввода) и Attach/Detach к портам.
Настройки IO хранятся отдельно от управляемого приложения (по умолчанию — IOConfigSDL.json, управляемое приложение использует собственный IOConfig.json), поэтому настройки никогда не пересекаются. При запуске эмулятор загружает настройки из того же файла и подключает настроенные устройства.
Примечание: в SDL-сборке у виртуальных контроллеров нет экранных диалогов; ими управляют привязанные события клавиатуры/геймпада, как обычными контроллерами.
Источник: Breaknes/BreaksCore/Readme.md, Breaknes/Breaknes/Readme.md, Breaknes/BreaknesSDL/Readme.md · страница входит в сайт документации.