ссылки на эмуляторы готовые (желательно самые простые)
Готовые эмуляторы в основном сами те еще раритеты годов написания этак 1995-2005 (золотой век эмуляторов давно прошел). Год назад переписывался с Sega-энтузиастом, его отзыв на то как обстоят дела с ретрохакингом:
Самое обидное в SMD-ретрохакинге — что с FOSS там очень плохо.
Gens, включая форк с выводом кишок — несвободный. Когда-то была Kega Fusion, она просто говнище. GPGX открытый, но несвободный, там от предыдущего автора осталась non-commercial-лицензия, и код такой из 90-х, трудно куда-то это встраивать, чтобы через год отребейзить и нифига не развалилось. Но это лучшее, что есть. BizHawk тоже позволяет смотреть на кишки, в качестве smd core там тоже форк GPGX, но его пишут .NETеры какие-то волшебные, он без Windows не работает.
Если хочется сами игоры конструировать, то вообще все принятые сообществом ромхак-инструменты — это win-specific говнище без исходников.
ну и желательно документацию - где её можно найти.
Информацию про то как писать всякие штуки на C++ (в т.ч. то что использовалось для симулятора) я иногда публикую в своем канале https://t.me/cxx95 (в теле статьи о нем не упоминал, чтобы не навязывать рекламу).
При эмуляции учитывается количество затрачиваемых циклов процессора на выполнение команд?
Нет, команды выполняются всё время без ограничений, пока не наступит VBLANK-прерывание. По идее ничего плохого не произойдет, просто игра будет много времени проводить в функции наподобии WaitVBLANK.
Как синхронизируется переферия сидящая на общей шине с процессором? Есть какое то архитектурное решение или просто все заинтересованные лица мониторят сколько циклов прошло?
Всё работает в один поток - есть цикл, который делает эти вещи:
poll_events - проверить события GUI, если видим что окно закрыли (нажали на крестик) то цикл прерывается и программа завершается.
update_controller - насобирать информацию с периферии, то есть про нажатия кнопок с клавиатуры и геймпада, и поставить флаги про это в "контроллер". А он в свою очередь реализует интерфейс Device и потом будет отдавать информацию про нажатия когда BusDevice ему передаст команду на read/write.
execute - постоянно исполнять инструкции пока верно условие или пока не получим информацию что мы прыгнули на VBLANK
video_.update() - вычисляет пиксели на игровом экране
render() - рисует весь GUI, в том числе игровой экран
Я новичок в вопросах эмулятостроения поэтому интересен опыт других.
Для меня это тоже первый эмулятор и я в другие эмуляторы не смотрел особо, пробовал позапускать, но с Linux они все как-то плохо дружат
+1, в моем опыте при программировании BLE делалось так: read-характеристики ничего интересного не отдавали (что-то уровня "id девайса" который и так известен), write-характеристики требовали поле с одноразовым ключом идентифицирующим юзера плюс весь запрос дополнительно шифровался и слушатель посылал http-запрос в бэкенд для расшифровки.
То что в статье это детский сад - взяли байты, посылавшиеся со смартфона (с одного компьютера) и отослали их же откуда-то еще (с другого компьютера), в цикле. Flipper Zero незачем, так можно послать программкой с компьютера где есть ble-чипсет.
Когда играл в нее, выдумал технику, основанную на том, что в отрядах необязательно должен быть герой - можно наплодить отряды по одному стрелку и спидранить исследование карты, заходить ими в мутные порталы и прочее.
Еще там был имбовые юниты, например "вампир" - им можно было разгромить любой отряд за счет воскрешения, когда забираешь жизни противника и он тебе не успевает наносить равносильный урон.
В третьих героев так и не поиграл, уже тогда (~2009 год) мне казалось что там графон прямо супер устаревший.
Мы написали императивный код который описывает что надо сделать, а оптимизатор делает это по своему
Оптимизатор сделал ровно то, что требуется. Проанализировал граф зависимостей между переменными и выяснил, как его можно переупорядочить и упростить, не нарушая инварианты по side effects, которые были в исходном коде. Код от этого не перестал быть императивным.
Может пора уже переходить на декларативный уровень?
Тогда C++ это уже супер декларативный уровень, потому что в ассемблере нет никаких классов, лямбд, исключений. Инварианты для компилятора тоже можно наставить [[assume]].
в C++ есть довольно большой зоопарк предположений (не всегда верных, даже в большинстве случаев не верных) на которых он строит свои оптимизации
C++ это достаточно зрелый язык, что у него есть модель памяти (у модного Rust ее нет...) Большинство предположений соответствуют ей и строги математически.
Например, если читать из одного и того же адреса в цикле, то C++ соптимизирует это в одно чтение, предполагая, что незачем читать больше одного раза одно и то же - нет side effect. Чтобы указать, что это не так (например, на железке в этот адрес какой-то другой процесс может записывать числа) и каждое чтение это наличие side effect, нужен модификатор volatile.
Для полноты рассказа (для тех, кто на "вы" с компиляторами) можно сказать, что язык Rust имеет к теме статьи очень посредственное отношение (это может быть важно знать).
В тулчейне LLVM есть название "фронтенд" для программы, которая переводит исходный код в LLVM IR, и "бэкенд" для программы, которая оптимизирует LLVM IR и переводит его в ассемблер.
Для нового языка программирования достаточно написать только "фронтенд", то есть переводчик исходника в LLVM IR. Сначала "фронтенд" был только один - Clang (для языков C/C++/Objective-C), затем появились другие для новых языков, в том числе для Rust.
Другими словами, примерные программы в статье могли бы быть написаны вместо Rust на C, C++ или куче других языков, а смысл остался бы тем же.
История про талантливого человека, который смог воспользоваться возможностями и реализовать свой потенциал. Жаль, что куда больше не смогли и вместо интересной работы наблюдали за окном постановку Балабанова, по итогу умерев в нищете. Остается догадываться, как могла бы выглядеть Богоспасаемая страна, будь там более удачный строй, который позволял бы условному Сергею Брину реализоваться "не отходя от кассы".
Единственное наблюдение - я когда-то прочитал все интервью Степанова, которые смог найти, и везде он очень неохотно говорил о процессе эмиграции - просто уехал и все. Неясно, зачем было скрывать данные о своем "правильном происхождении", если сейчас от него одни плюсы. Например, паспорт с безвизом во весь мир.
Проводилось по пять запусков, но представлены только наименьшее и наибольшее время, а также среднее пропорциональное для всех пяти запусков.
Запусков очень мало.
В матстате никто не считает минимум и максимум. Это уровень детского сада. Считать надо дисперсию и среднеквадратичное отклонение. В идеале это должны быть маленькие значения, а распределение времен нормальным.
Из интервью с Анатолием Карповым (чемпион мира по шахматам 1975-2985) из 2006 года, когда речь зашла о том, что компьютер обыгрывает человека в шахматы:
Мы оказывается в неравных условиях, потому что в памяти ЭВМ заложена информация, собранная за всю историю шахмат, а шахматист пользоваться ей не имеет права. Значит, надо либо изъять эту базу данных у компьютера, либо и нам обеспечить доступ к ней в ходе партии. Я уж не говорю о том, что время необходимо выравнивать...
... Нужно просто очень серьезно подходить к подготовке и играть с любым «Deep Though» или «Deep Blue», как на первенство мира. К сожалению, большинство гроссмейстеров видят в этих поединках лишь дополнительную возможность заработка и не понимают, что вредят своему будущему, дискредитируют шахматы слабой подготовкой и не очень мудрой игрой. Именно из-за столь легковесного подхода у простых болельщиков, а тем более у людей, далеких от шахмат, сложилось впечатление, что компьютер намного сильнее и умнее человека. Нет, это, к счастью, не так.
Тогда, подставляя вместо EnumType разные типы данных (например FromInt<Color>(100500), мы получим разные функции - по одной функции на каждый EnumType. Шаблонная функция (и вообще шаблонный код) это просто заготовка реального кода.
Таким образом, кодогенератор создает определения подобных частных функций рядом с исходниками, и затем компилятор связывает вызов шаблонной функции с конкретным определением функции.
В какой-то совсем внешней библиотеке эти определения находиться не смогут, потому что внешние библиотеки не имеют никакого понятия про типы данных в конкретной программе. Пусть внутри программы есть тип enum Foo, тогда программа должна сама заботиться о наличии определения функции FromInt<Foo>.
Плохо работает - все файлы просто один за другим "склеиваются" в один большой файл, поэтому локальные имена конфликтуют.
Если через некоторое время отказаться от данного подхода, то придется руками написать овер9000 новых include, которые раньше не писали, потому что их подключал какой-нибудь другой файл, который в "склейке" идет раньше.
купить максимальное количество лотов за минимальные деньги
А это не задача о рюкзаке? Часто встречается в задачках по программированию - правда, в таких ограничениях, чтобы можно было найти оптимальное решение.
Не вопрос стандарта языка, аби хранят реализации, а в стандарте такого понятия нет
Ситуация из разряда "жопа есть, а слова нету". Почему нельзя удалить из стандарта ставший ненужным с C++11 метод push_back? (А точнее, переписать его код вместо создания нового метода emplace_back)
Потому что ABI сломается. Хотя, казалось бы, про ABI в стандарте ничего не написано.
Я бы, кстати, еще AST (Abstract Syntax Tree) в стандарт "C++ мечты" легализовал, чтобы люди могли писать тулзы для исходного кода без привязки к внутренностям компилятора.
Зато есть куча другого кода вне стандарта
95% людей устроило бы наличие любой (даже не самой быстрой) json-либы (как в питоне). Если бы была нужна какая-то мега-быстрая json-либа (в realtime-приложениях), то там уже могут быть варианты, но свои трейд-оффы есть везде.
Предложенное статье сокращает количество правил. Значительно
Предложенное в статье главным образом создает какой-то совершенно другой язык.
Достаточно быстрая компиляция, относительно возможностей языка.
Я имел в виду "класс" проблем, которые именно вызывают медленную компиляцию. Например, из-за некоторых свойств препроцессора тулза include-what-you-use в 10 раз толще, чем нужно быть, и все равно работает не совсем как следует. Значит, нужно порезать препроцессор. Просто не стал все это расписывать, иначе комментарий получился бы длиннее статьи.
Cmake
Многие считают это худшей системой сборки ever. Если приводить все аргументы, выйдет тоже немаленький текст, но я читал бомбежку на CMake (сам его редко использую) - меня впечатлило.
Они не должны быть встроенными, потому что IDE и спелчекеры не должны являтся частью языка. Как и система сборки и документирования. Сторонние инструменты есть
95% людей были бы согласны на любой нормальный спеллчекер в "C++ мечты". Да и что значит "не должны", "не могут". C#/Java/Python/Rust - у них все есть и они все могут.
Некоторые пункты отдают легким троллингом. Я бы еще предложил оставить в функциях не более 1 аргумента, а вызов функций с N аргументами имитировать в виде шаблонной магии.
Реальные проблемы в C++ выглядят не так. Вот так выглядят "классы" проблем, после фикса которых можно получить "C++ мечты":
Вопрос о сломе ABI и всего веера положительных и отрицательных эффектов из-за этого.
Бедность стандартной библиотеки по сравнению с буквально всеми другими языками (тут ABI ни при чем).
Поехавший нейминг и правила, например овер9000 значений слова static при разных обстоятельствах; или что anonymous namespace наделяет все символы внутри себя internal linkage-ом (как до такого додумались?).
Медленная компиляция по разным причинам.
Отсутствие общепринятого менеджера пакетов/библиотек (тут могут быть разные мнения, но это тоже "класс" проблем)
Достаточно медленное развитие языка в целом - те же концепты пытались родить с нулевых годов, а также медленная поддержка этого в компиляторах.
Отсутствие встроенных линтеров и чекеров (которых овер9000, но ими пользуются не только лишь все, а только самые продвинутые).
А "фиксы", которые удаляют ООП и т.д. - это не решение всех проблем C++, а описание какого-то нового языка.
Есть много кейсов, где может применяться NRVO. В paper описано 20 кейсов, сейчас Clang делает это только в 13 кейсах (13/20). Мой патч добавил бы еще 4 кейса (было бы 17/20).
Сейчас ни один компилятор не поддерживает все описанные в paper кейсы. Я отправлял патч в Clang, чтобы покрыть на 20% больше кейсов, чем поддерживается сейчас, но до конца не добил тему, потому что патчи медленно принимаются.
6-кнопочный сейчас поддержал в коммите, там просто муторная система для поддержки обратной совместимости с 3-кнопочным - описание на plutiedev
Проверил на игре Ultimate Mortal Kombat 3 (1996) - на скриншоте нажатие кнопки Z которая делает high kick
Готовые эмуляторы в основном сами те еще раритеты годов написания этак 1995-2005 (золотой век эмуляторов давно прошел). Год назад переписывался с Sega-энтузиастом, его отзыв на то как обстоят дела с ретрохакингом:
Пробовал читать "официальную документацию Sega" (на segaretro есть), она жуткая, намного удобнее и понятнее читать неофициальные сайты - segaretro, plutiedev, rasterscroll.
Информацию про то как писать всякие штуки на C++ (в т.ч. то что использовалось для симулятора) я иногда публикую в своем канале https://t.me/cxx95 (в теле статьи о нем не упоминал, чтобы не навязывать рекламу).
Нет, команды выполняются всё время без ограничений, пока не наступит VBLANK-прерывание. По идее ничего плохого не произойдет, просто игра будет много времени проводить в функции наподобии
WaitVBLANK
.Всё работает в один поток - есть цикл, который делает эти вещи:
poll_events
- проверить события GUI, если видим что окно закрыли (нажали на крестик) то цикл прерывается и программа завершается.update_controller
- насобирать информацию с периферии, то есть про нажатия кнопок с клавиатуры и геймпада, и поставить флаги про это в "контроллер". А он в свою очередь реализует интерфейсDevice
и потом будет отдавать информацию про нажатия когда BusDevice ему передаст команду на read/write.execute
- постоянно исполнять инструкции пока верно условие или пока не получим информацию что мы прыгнули на VBLANKvideo_.update()
- вычисляет пиксели на игровом экранеrender()
- рисует весь GUI, в том числе игровой экранДля меня это тоже первый эмулятор и я в другие эмуляторы не смотрел особо, пробовал позапускать, но с Linux они все как-то плохо дружат
+1, в моем опыте при программировании BLE делалось так: read-характеристики ничего интересного не отдавали (что-то уровня "id девайса" который и так известен), write-характеристики требовали поле с одноразовым ключом идентифицирующим юзера плюс весь запрос дополнительно шифровался и слушатель посылал http-запрос в бэкенд для расшифровки.
То что в статье это детский сад - взяли байты, посылавшиеся со смартфона (с одного компьютера) и отослали их же откуда-то еще (с другого компьютера), в цикле. Flipper Zero незачем, так можно послать программкой с компьютера где есть ble-чипсет.
Когда играл в нее, выдумал технику, основанную на том, что в отрядах необязательно должен быть герой - можно наплодить отряды по одному стрелку и спидранить исследование карты, заходить ими в мутные порталы и прочее.
Еще там был имбовые юниты, например "вампир" - им можно было разгромить любой отряд за счет воскрешения, когда забираешь жизни противника и он тебе не успевает наносить равносильный урон.
В третьих героев так и не поиграл, уже тогда (~2009 год) мне казалось что там графон прямо супер устаревший.
Оптимизатор сделал ровно то, что требуется. Проанализировал граф зависимостей между переменными и выяснил, как его можно переупорядочить и упростить, не нарушая инварианты по side effects, которые были в исходном коде. Код от этого не перестал быть императивным.
Тогда C++ это уже супер декларативный уровень, потому что в ассемблере нет никаких классов, лямбд, исключений. Инварианты для компилятора тоже можно наставить [[assume]].
C++ это достаточно зрелый язык, что у него есть модель памяти (у модного Rust ее нет...) Большинство предположений соответствуют ей и строги математически.
Например, если читать из одного и того же адреса в цикле, то C++ соптимизирует это в одно чтение, предполагая, что незачем читать больше одного раза одно и то же - нет side effect. Чтобы указать, что это не так (например, на железке в этот адрес какой-то другой процесс может записывать числа) и каждое чтение это наличие side effect, нужен модификатор
volatile
.Для полноты рассказа (для тех, кто на "вы" с компиляторами) можно сказать, что язык Rust имеет к теме статьи очень посредственное отношение (это может быть важно знать).
В тулчейне LLVM есть название "фронтенд" для программы, которая переводит исходный код в LLVM IR, и "бэкенд" для программы, которая оптимизирует LLVM IR и переводит его в ассемблер.
Для нового языка программирования достаточно написать только "фронтенд", то есть переводчик исходника в LLVM IR. Сначала "фронтенд" был только один - Clang (для языков C/C++/Objective-C), затем появились другие для новых языков, в том числе для Rust.
Другими словами, примерные программы в статье могли бы быть написаны вместо Rust на C, C++ или куче других языков, а смысл остался бы тем же.
Если не хочется, есть много альтернатив. Попробуйте оплатить YouTube Music или Spotify или Apple Music или SoundCloud. Они очень ждут ваших денег.
История про талантливого человека, который смог воспользоваться возможностями и реализовать свой потенциал. Жаль, что куда больше не смогли и вместо интересной работы наблюдали за окном постановку Балабанова, по итогу умерев в нищете. Остается догадываться, как могла бы выглядеть Богоспасаемая страна, будь там более удачный строй, который позволял бы условному Сергею Брину реализоваться "не отходя от кассы".
Единственное наблюдение - я когда-то прочитал все интервью Степанова, которые смог найти, и везде он очень неохотно говорил о процессе эмиграции - просто уехал и все. Неясно, зачем было скрывать данные о своем "правильном происхождении", если сейчас от него одни плюсы. Например, паспорт с безвизом во весь мир.
Придумал C#-edition этого комментария:
Запусков очень мало.
В матстате никто не считает минимум и максимум. Это уровень детского сада. Считать надо дисперсию и среднеквадратичное отклонение. В идеале это должны быть маленькие значения, а распределение времен нормальным.
Из интервью с Анатолием Карповым (чемпион мира по шахматам 1975-2985) из 2006 года, когда речь зашла о том, что компьютер обыгрывает человека в шахматы:
Можно считать, что сам C++ это тоже кодогенератор для ассемблера.
Это особенность работы с шаблонами в C++. Пусть есть объявление такой шаблонной функции:
Тогда, подставляя вместо
EnumType
разные типы данных (напримерFromInt<Color>(100500)
, мы получим разные функции - по одной функции на каждыйEnumType
. Шаблонная функция (и вообще шаблонный код) это просто заготовка реального кода.Вывод компилятора с двумя функциями
Таким образом, кодогенератор создает определения подобных частных функций рядом с исходниками, и затем компилятор связывает вызов шаблонной функции с конкретным определением функции.
В какой-то совсем внешней библиотеке эти определения находиться не смогут, потому что внешние библиотеки не имеют никакого понятия про типы данных в конкретной программе. Пусть внутри программы есть тип
enum Foo
, тогда программа должна сама заботиться о наличии определения функцииFromInt<Foo>
.Плохо работает - все файлы просто один за другим "склеиваются" в один большой файл, поэтому локальные имена конфликтуют.
Если через некоторое время отказаться от данного подхода, то придется руками написать овер9000 новых include, которые раньше не писали, потому что их подключал какой-нибудь другой файл, который в "склейке" идет раньше.
А это не задача о рюкзаке? Часто встречается в задачках по программированию - правда, в таких ограничениях, чтобы можно было найти оптимальное решение.
Ситуация из разряда "жопа есть, а слова нету". Почему нельзя удалить из стандарта ставший ненужным с C++11 метод push_back? (А точнее, переписать его код вместо создания нового метода
emplace_back
)Потому что ABI сломается. Хотя, казалось бы, про ABI в стандарте ничего не написано.
Я бы, кстати, еще AST (Abstract Syntax Tree) в стандарт "C++ мечты" легализовал, чтобы люди могли писать тулзы для исходного кода без привязки к внутренностям компилятора.
95% людей устроило бы наличие любой (даже не самой быстрой) json-либы (как в питоне). Если бы была нужна какая-то мега-быстрая json-либа (в realtime-приложениях), то там уже могут быть варианты, но свои трейд-оффы есть везде.
Предложенное в статье главным образом создает какой-то совершенно другой язык.
Я имел в виду "класс" проблем, которые именно вызывают медленную компиляцию. Например, из-за некоторых свойств препроцессора тулза include-what-you-use в 10 раз толще, чем нужно быть, и все равно работает не совсем как следует. Значит, нужно порезать препроцессор. Просто не стал все это расписывать, иначе комментарий получился бы длиннее статьи.
Многие считают это худшей системой сборки ever. Если приводить все аргументы, выйдет тоже немаленький текст, но я читал бомбежку на CMake (сам его редко использую) - меня впечатлило.
95% людей были бы согласны на любой нормальный спеллчекер в "C++ мечты". Да и что значит "не должны", "не могут". C#/Java/Python/Rust - у них все есть и они все могут.
Некоторые пункты отдают легким троллингом. Я бы еще предложил оставить в функциях не более 1 аргумента, а вызов функций с N аргументами имитировать в виде шаблонной магии.
Реальные проблемы в C++ выглядят не так. Вот так выглядят "классы" проблем, после фикса которых можно получить "C++ мечты":
Вопрос о сломе ABI и всего веера положительных и отрицательных эффектов из-за этого.
Бедность стандартной библиотеки по сравнению с буквально всеми другими языками (тут ABI ни при чем).
Поехавший нейминг и правила, например овер9000 значений слова
static
при разных обстоятельствах; или что anonymous namespace наделяет все символы внутри себя internal linkage-ом (как до такого додумались?).Медленная компиляция по разным причинам.
Отсутствие общепринятого менеджера пакетов/библиотек (тут могут быть разные мнения, но это тоже "класс" проблем)
Достаточно медленное развитие языка в целом - те же концепты пытались родить с нулевых годов, а также медленная поддержка этого в компиляторах.
Отсутствие встроенных линтеров и чекеров (которых овер9000, но ими пользуются не только лишь все, а только самые продвинутые).
А "фиксы", которые удаляют ООП и т.д. - это не решение всех проблем C++, а описание какого-то нового языка.
Есть много кейсов, где может применяться NRVO. В paper описано 20 кейсов, сейчас Clang делает это только в 13 кейсах (13/20). Мой патч добавил бы еще 4 кейса (было бы 17/20).
https://reviews.llvm.org/D119792 - тут я описал, что делаю.
Возражения там по делу, но просто стало влом тащить до конца, потому что КПД контрибьютинга в подобные опенсорсные проекты очень низок.
Это хорошая тема! Антон Жилин писал paper (предложение в Стандарт) о гарантированном NRVO: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2025r2.html
Сейчас ни один компилятор не поддерживает все описанные в paper кейсы. Я отправлял патч в Clang, чтобы покрыть на 20% больше кейсов, чем поддерживается сейчас, но до конца не добил тему, потому что патчи медленно принимаются.