Мой проект core5277 и с чем его едят
Данный проект не имеет никакого отношения к коммерции.
Я с детских лет любил кодить на асме, тогда доступтыми машинами были ZX Spectum'ы на микропроцессорах Z80. Позже мне пришлось отказаться от Спекки в сторону IBM PC, в универах на Спекки не писали. Да, я писал 'кое-что' на асме для таких как Intel 386, но это не приносило удовольствия в сравнении со Спекки.
Из-за университета мне пришлось годами писать на Си и это было не приятно, потому что впринципе восприятие низкоуровневого программирования сильно отличалось. Достаточно сказать, что в асме нет указателей, повторяю, их нет, есть индексные регистры.
Лично мое мнение - в сфере ИТ слишком мало профессиональных, умных решений, решений от гениальных, талантливых людей желающих творить. Поэтому я испытываю большую ностальгию по тем временам. В том числе я боготворю Sun Microsystems, там был тот-же дух созидания, что и на Спекки.
Данный проект родился из-за моей ностальгии по тем временам.
В нем я предлагаю ассемблер наработки(также у меня есть свой ассемблер-сборщик, так как avr assembler не справляется с моими проектами). По сути это операционная система реального времени с ядром и дополнительными утилитами реализованными для Atmel AVR микроконтроллеров на чистом ассемблере.
Я предлагаю воспользоваться моими наработками желающим познакомиться с ассемблером для AVR. Эти наработки могут значительно уменьшить порог вхождения в данное направление.
Думаю мой прект позволит вам создавать многие проекты не намного сложее чем на Си.
P.S. Я перестал поддерживать данный проект(в открытом доступе) по некторым социальным причинам, но вы можете воспользоваться тем, что я сделал ранее.
Спасибо за внимание.
Assembler и C в рамках маломощных микроконтроллеров
У меня есть свой крупный проект по автоматизации, который можно применить для умного дома.
И есть линейка своих собственных исполнительных устройств, модулей, датчиков и т.п. реализованных на AVR(в основном это ATmega168).
При этом данная линейка - это хобби, ее я реализую для себя и вкладываю туда функционал нужный мне, и пишу соответственно на том, что нравится мне. А не Вам, и не ради моды.
Меня часто спрашивают, почему я пишу прошивки для AVR микроконтроллеров на ассемблере.
Часто слышу стереотипы - ассемблер никому не нужен, очень сложный, нет переносимости, большие трудозатраты. И даже такие, как - на нем никто не пишет и Вы не должны, в общем я кому-то, получается, что-то должен.
Первое и пожалуй главное - ничто не научит работать с периферией лучше, чем кодинг на ассемблере.
Я применяю МК по прямому назначению - работа с периферией, если мне потребуется рисовать UI или считать математику я буду смотреть в сторону SoC или вообще в сторону микрокомпьютеров типа всяческих PI, где буду использовать более подходящий высокоуровневый язык чем Си.
Кстати по этой причине мне не особо интересны STM микроконтроллеры.
Второе - ассемблер не сложный, он имеет всего несколько десятков команд, это сложно запомнить? Главная сложность в том, что на ассемблере программист многие алгоритмы составляет сам, чем и добивается существенной оптимизации. Т.е нужно уметь думать головой. И дело не в том, что компилятор что-то может или не может, дело в принципе в подходе к самому программированию. В Си вы не задумываясь воспользуетесь тяжелой функцией, вместо пары тройки команд на ассемблере.
Более того, у вас буквально ассемблер с несколькими десятками команд, чистая периферия МК и все, ничего лишнего. Вам не нужно знать третье звено - как работает компилятор! А в Си Вам придется с ним разбираться, если конечно вы не пишете студенческую работу и ограничены в ресурсах МК.
Третье, как вообще можно отлаживать даже программу на Си для МК, не зная ассемблера? Как можно написать оптимальный бутлоадер не зная ассемблера? Как посчитать такты в критических по времени местах? Как на 512 байт ОЗУ контролировать ресурсы(тот-же stack) в крупном проекте? Да никак, поэтому вы все на Си и на STM где больше ресурсов. Потому что тупо не можете. А я вот например могу на Cortex асме писать код для STM и периферию я их гораздо лучше понял взявшись за асм после Си.
Четвертое - трудозатратно. Не особо, возможно на багфиксинг утечек памяти на Си вы потратите не намного меньше времени, чем я на реализацию на асме. Здесь ключевое - наработки - сообщество Си наработало огромный пласт кода, и вы не задумываясь его используете считая свой код основными трудозатратами. При этом говоря про асм все почему-то думают о пустом листе, на котором все, все, все нужно писать с нуля. С чего бы? Переносимость? Об этом ниже. На асм можно точно также наработать(и это было ранее) все теже функции, что и вы используете в Си. Но нет, популяризация Си свела на нет наработки на асм.
Пятое - переносимость кода, да это главный минус в асм. Однако умение строить алгоритмы существенно снижает трудозатраты. Так же как и разделение кода на уровни. Я лично наработал операционку с кучей функций, я не пишу математику и другие процедуры с нуля, я их просто подключаю и вызываю также как это делаете вы в Си. Более того, мой код(особенно код верхнего уровня) без проблем переносится между МК семейства AVR. И потом, как вы вообще представляете перенос сложной программы на Си с AVR скажем на STM, да никак. Где ваша хваленая переносимость Си?
Если мне нужно что-то по быстрому накидать для периферии - я возьму микрокомпьютер и за минуты накидаю на Java то, на что вы потратите в несколкьо раз больше времени на Си.
Если мне нужна сложная логика - я не буду это делать на микроконтроллере, я возьму что-то типа SoC или опять же микрокопьютер.
Если мне нужна примитивная логика для работы с периферией да еще и с минимальными затратами, я возьму МК и накидаю быстро вызовы уже реализованных процедур на асме.
Я не вижу по большей части где и для чего использовать Си. Это просто мода.
Даже используя ESP я предпочту микро питон, а не все эти глючные библиотеки HAL написанные на си, хотя наверное вы привыкли и не замечаете какой откровенно плохой код вы используете.
А если мне нужно будет говнокодить, с целью быстренько что-то где-то копипастить и выдать за свое решение, срубить бабла по бытрому, тогда да, я возьму STM и буду писать на Си. Но мне такая работа не интересна.
Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI?
Зачастую в процессе разработки собственных устройств или моддинга уже существующих, встаёт задача выполнения стороннего кода: будь то ваши собственные программы с SD-флэшек, или программы, написанные другими пользователями с помощью SDK для вашего устройства. Тема компиляторов и кодогенерации достаточно сложная: чтобы просто загрузить ELF или EXE (PE) программу, вам нужно досконально разбираться в особенностях вашей архитектуры: что такое ABI, релокации, GOT, отличие -fPIE от -fPIC, как писать скрипты для ld и т. п. Недавно я копал SDK для первых версий Symbian и основываясь на решениях из этой ОС понял, каким образом можно сделать крайне «дешевую» загрузку любого нативного кода практически на любом микроконтроллере, совершенно не вникая в особенности кодогенерации под неё! Сегодня мы с вами: узнаем, что происходит в процессе загрузки программы ядром Linux, рассмотрим концепцию, предложенную Symbian Foundation и реализуем её на практике для относительно малоизвестной архитектуры — XTensa (хотя она используется в ESP32, детали её реализации «под капотом» для многих остаются загадкой). Интересно? Тогда добро пожаловать под кат!
❯ Как это работает?
Думаю, для многих моих читателей реализация процесса загрузки exe-программ и dll-библиотек в память процесса оставалась эдаким чёрным ящиком, в детали реализации которого вдаваться не нужно. Отчасти это так и есть: современные ОС разруливают процесс загрузки бинарников в память сами, не требуя от программиста вообще ничего, даже понимания того, куда будет загружена его библиотека или программа.
Давайте для общего понимания вкратце разберемся, как происходит загрузка программ в Windows/Linux:
1. Система создаёт процесс и загружает в память программы секции из ELF/PE. Обычные программы для своей работы используют 3 секции: .text (код), .data (не-инициализированный сегмент памяти для глобальных переменных), .bss (сегмент памяти для инициализированных переменных). Каждому процессу выделяется собственное адресное пространство, называемое виртуальной памятью, которое не позволяет программе испортить память ядра, а также позволяет не зависеть от разметки физической памяти на выполняющей машине. Концепцию виртуальной памяти реализует специальной модуль в процессоре, называемый MMU.
2. Если бы наши программы не использовали никаких зависимостей в виде динамических библиотек, то на этом процесс загрузки можно было бы закончить: каждая программа имеет свой адрес загрузки, относительно которого линкер строит связи между обращениями к коду/данным программы. Фактически, для самых простых программ линкеру остаётся лишь прибавить адрес загрузки программы (например, 0x100) к каждому абсолютному обращению к памяти.
Однако современные программы используют десятки библиотек и для всех предусмотреть собственный адрес загрузки не получится: кто-то где-то всё равно будет пересекаться и вероятно, портить память. Кроме того, современные стандарты безопасности в Linux рекомендуют использовать позиционно-независимый код, дабы использовать преимущества ASLR (Address Space Layout Randomization, или простыми словами возможность загрузить программу в случайное место в памяти, дабы некоторые уязвимости, завязанные на фиксированном адресе загрузки программы перестали работать).
3. Поэтому для решения этой проблемы придуман т. н. динамический линкер, который уже на этапе загрузки программы или библиотеки патчит программу так, чтобы её можно было загрузить в любой участок памяти. Для этого используются данные, полученные от обычного линкера а этапе компиляции программы: помимо .text, .data и .bss, линкер создаёт секции .rel и .rel-plt, которые называются релокациями. Если объяснять совсем условно, то релокации — это просто запись вида «какой абсолютный адрес в коде программы нужно пропатчить» -> «на какое смещение его пропатчить». Самая простая релокация выглядит вот так:
Где по итогу:
.rel-plt же служит для резолвинга вызовов к dll/so: изначально программа ссылается на заранее определенные в процессе компиляции символы, которые уже в процессе загрузки патчатся на физические адреса функций из загруженной библиотеки.
И казалось бы — всё очень просто, пока в дело не вступают GOT (Global Offset Table — глобальная таблица смещений) и особенности реализации конкретного ABI. И ладно бы x86 или ARM, там всё разжевано и понятно, однако на других архитектурах начинаются проблемы и не всегда очевидно что и где за что отвечает.
А ведь чаще всего нужно просто загрузить небольшую программу, которой не нужны комплексные загрузчики: немного кода, немного данных и всё. И тут у нас есть три выхода:
Писать полноценный загрузчик ELF-бинарников. ELF может оказаться громоздким для некоторых окружений и его реализация может оказаться тривиальной не для всех.
Зарезервировать определенный сегмент в памяти (пусть с 0xFFF по 0xFFFF) и скомпилировать нашу программу с адресом загрузки 0xFFF с параметром -fno-pic. В таком случае, линкер сгенерирует обращения к памяти по абсолютным адресам — если переменная лежит по адресу 0xFFF, то программа будет обращаться сразу к этому адресу памяти, без необходимости что либо динамически линковать. Именно такой подход использовался во времена ZX Spectrum, Commodore 64 и MS-DOS (однако там роль «виртуальной памяти» выполняла такая особенность 8086, как сегменты). У такого подхода есть и минусы: относительная невозможность загрузки сразу нескольких программ одновременно, зарезервированное пространство линейно отъест небольшой кусок памяти у основной прошивки, нет возможности динамической аллокации секций. Зато такой код теоретически будет работать быстрее, чем PIC.
Проблемы реализации такого способа: иногда нужно лезть в систему сборки основной прошивки и патчить скрипт линкера так, чтобы он не трогал определенный регион памяти. В случае esp32, например, это требует патча в сам SDK и возможного «откола» от мейнлайн дистрибутива.Использовать программу с относительной адресацией, однако без сегментов .bss и .data. Самый простой в реализации способ, который к тому же очень экономичен к памяти, позволяет загружать программу в любое место и пользоваться всеми фишками динамического аллокатора и не требует вмешательств в основную прошивку, кроме примитивного загрузчика программ. Именно его я и предлагаю рассмотреть подробнее.
Недавно мы сидели в чате ELF-сцены (разработка нативных программ под телефоны Siemens, Sony Ericsson, Motorola и LG с помощью хаков) и думали, как же можно реализовать загрузчик сторонних программ на практически неизвестных платформах. Кто-то предлагал взять ELF под основу — однако с его реализацией под некоторые платформы есть трудности, а кто-то предлагал писать «бинлоадер» — самопальный формат бинарников, который получается из, например, тех же эльфов.
В это же время я копал SDK для Symbian и хорошо помнил, что в прикладных приложениях для этой ОС нет поддержки глобальных переменных вообще. Да, сегмент .data и .bss полностью отсутствует — переменные предлагается хранить в структурах. Почему так сделано? Всё дело в том, что каждая программа в Symbian — это dll-библиотека, которую загружает EKA и создаёт экземпляр CApaApplication. И дабы была возможность загрузить dll один раз для всех программ (что справедливо для системных библиотек), ребята полностью выкинули возможность использования любых глобальных переменных. А ведь идея интересная!
Однако в таком подходе есть несколько серьезных ограничений:
Отсутствие глобальных переменных может стать проблемой при портированиии уже существующего софта, хотя вашим программам ничего не мешает передавать в каждую функцию структуру с глобальным стейтом, который можно при необходимости изменять. Кроме того, нет ограничений на использование C++ (за исключением необходимости ручной реализации new/delete и отсутствием исключений).
Отсутствие преинициализированных данных. Вот это уже может стать относительно серьёзной проблемой, у которой, тем не менее, есть свои обходные решения. Например если вы храните команды для инициализации дисплея в таблице, или какие-либо калибровочные данные — вы не сможете их объявить, просто используя инициализаторы в C. Тоже самое касается и строковых литерал. Тут есть два варианта: часть таблиц можно вынести на стек (если эти самые таблицы достаточно маленькие), либо подгружать необходимые данные из бинарника с помощью основной прошивки (например, LoadString и т. п.).
Давайте же на практике посмотрим, имеет ли право на жизнь такой подход!
❯ Практическая реализация
Формат нашего бинарника будет до безобразия прост: небольшой заголовок в начале файла и просто сырой дамп сегмента .text, который можно экспортировать из полученного elf даже без необходимости писать скрипт для линкера. При этом нужно учесть, что ESP32 — это микроконтроллер частично Гарвардской архитектуры, т. е. шина данных и кода у него расположены отдельно. Однако у чипа есть полноценный MMU, который позволяет маппить регионы физической памяти в виртуальную память, чем мы и воспользуемся в итоге!
Заголовок нашего бинарника будет выглядеть вот так:
Программа общается с основной прошивкой посредством псевдо-syscall'ов: функции, которая в качестве первого аргумента ожидает номер нужной службы и один 32х-битный указатель для описания структуры с параметрами. Реализация syscall'ов — одна из самых простых и неприхотливых с точки зрения обратной совместимости с будущими прошивками.
Концептуально всё очень просто: GetGlobalStateSize сообщает нашему загрузчику размер структуры для хранения глобального стейта, в то время как Start уже фактически заменяет main() в нашей программе. Необходимости в crt0 нет, поскольку весь необходимый инит выполняет бутлоадер ESP32. Впрочем, при желании вы можете выделить отдельный стек для вашей программы — это повысит надежность, если выполняемая программа удумает испортить стек.
Собираем нашу программу:
xtensa-esp32-elf-cc.exe test.c -fno-pic -nostdlib -nostartfiles -Wl,--section-start=.text=0x0
xtensa-esp32-elf-objcopy.exe --only-section=.text --output-target binary a.out run.bin
-fno-pic отключает генерацию кода, зависимого от GOT, -nostdlib и -nostartfiles убирает из билда crt0 и stdlib, благодаря чему мы получаем только необходимый код. --section-start задает смещение для загрузки секции .text на 0x0 (в идеале это делать необходимо из скрипта для ld).
objcopy скопирует из полученного ELF только необходимую нам секцию .text.
Как же это работает на практике? Давайте дизассемблируем выходной бинарник и посмотрим, что у нас дает на выхлопе cc:
Обратите внимание, что Start вызывает подфункции с помощью инструкции CALLX8, которая в отличии от обычного Immediate-версии CALL8, выполняет переход относительно текущего адреса в PC, благодаря чему переход полностью независим от адреса загрузки программы в памяти. А благодаря тому, что все данные, в том числе и указатель на глобальный стейт передаются через стек, нет необходимости релокейтить сегменты данных.
По итогу всё, что нужно от загрузчика бинарников — это загрузить программу в память для инструкций, выделить память для структуры с стейтом программы и передать управление Start. Всё!
Конкретно в случае ESP32, у нас есть два возможных решения задачи загрузки программы в память:
Загрузить программу в IRAM. Такая возможность теоретически есть, однако на практике загрузчик ESP32 устанавливает права только на чтение и выполнение на данный регион памяти. Попытка что-то скопировать туда закончится исключением SIGSEGV. Кроме того, сегмент IRAM относительно небольшой — всего около 200Кб.
Самопрограммирование. Для этого, в esp32 есть два механизма — Partition API и SPI Flash API. Я выбрал Partition API для простоты реализации.
Для нашей прошивки необходимо будет переразметить флэш-память. Для этого запускаем idf.py menuconfig, идём в Partition Table -> Custom partition table CSV. Создаём в папке проекта partitions.csv, куда пишем:
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
executable, data, undefined, 0x110000, 0x10000
Для заливки программы можно использовать соответствующее Partition API, либо parttool.py:
parttool.py --port "COM41" write_partition --partition-name=executable --input "run.bin"
Переходим к загрузчику программы:
Прошиваем ESP32:
И смотрим результат:
SysCall 25
SysCall 35
SysCall 15
Всё работает!
❯ Заключение
Как видите, ничего сложного в выполнении сторонних программ при условии соблюдении некоторых ограничений нет. Да, в таком подходе есть как серьезные плюсы, так и минусы, однако он делает своё дело и позволяет реализовать запуск игр на кастомных игровых консолях, или сторонних программ на самодельных компьютерах. Ну и конечно же не стоит забывать про плагины! Авось в вашем решении нужна возможность расширения функционала устройства, однако предоставлять исходный код или даже объектные файлы нет возможности — тогда вам может пригодится и такая методика.
Пожалуй, стоит упомянуть ещё один… очень своеобразный метод, который я иногда встречаю при реализации самодельных компьютеров. Люди пишут… эмуляторы 6502/Z80 :)
И если такой подход ещё +- применим к ESP32, то в AVR просадки производительности будут слишком серьезными. Так зачем, если можно использовать все возможности ядра на максимум?
Написал свою первую игру для Dendy (NES) - The Iron Steam
Всем привет. В этом посте я расскажу о своем первом опыте разработки игры для моей любимой консоли из детства под гордым названием денди (хотя раньше мы их приставками называли, но сейчас уже терминология устоялась). Далее вас ждет много интересного, так что заготавливайте вкусняшки.
Логотип консоли Dendy
История создания игры
Идея создать свою собственную игру для денди появлялась у меня довольно давно, но плотно занялся я этим вопросом только в конце 2022го года.
Основным стимулом для разработки игры послужило мое желание создавать свои модули расширения для консоли, так как для денди их практически не выпускалось, а у меня по этому поводу есть много идей и планов (модуль подключения обычно клавиатуры, модуль выхода в интернет и т.д.).
Изначально была поставлена задача написать хотя бы "Hello, World", который я мог бы запустить на эмуляторе. Но, начав читать переводной курс по разработке игр для денди на языке Си на хабре, мне удалось написать простейшую программу всего лишь за полчаса. И этого мне показалось слишком мало.
Более-менее освоив курс статей с хабра за 2-3 недели, в конце января 2023го было принято решение разработать пошаговую стратегию про стимпанковых мехов для закрепления навыков. Думал, что ограничусь созданием минимального геймплея, но разработка зашла немного дальше.
Такой сеттинг и жанр я выбрал, так как люблю пошаговые стратегии, стимпанк и игры про мехов (фронт мишн ван лав). Тем более у меня были наработки на эту тему в плане механик и немного лора (я делал наброски подобной игры для современных компов, но забросил).
В итоге с начала февраля 2023го началась активная разработка игры и продолжалась она до середины апреля (далее у меня появились более важные дела и игру пришлось отложить до лучших времен).
Игру я хотел анонсировать еще летом, так как базовые механики уже работоспособны и она проходима (хотя конкретно концовки у игры нет, можно просто пройти все уровни и прокачивать меха). Есть даже примерочный открытый мир, но он будет полностью перерисован и доработан.
Скриншот боевого геймплея актуальной версии игры
Особенности разработки игры для Денди
Разработка игр для NES/Famicom консолей является крайне специфическим занятием, так как требует понимания работы приставки на аппаратном уровне, что для многих является отталкивающим фактором. Но, так как у меня есть опыт разработки софта для микроконтроллеров, эта особенность для меня была не слишком критична.
Далее стоял вопрос о инструментах для разработки игры. Традиционно игры для денди пишут на ассемблере, так как ресурсы консоли очень ограничены и важен каждый такт процессора и каждый байт памяти. Я мог бы писать и на ассемблере, но это затянуло бы проектор и увеличило шансы к его забрасыванию на свалку истории.
В итоге я выбрал компилятор CC65, который позволяет писать код для денди денди на языке си. Этот компилятор поддерживается до сих пор и регулярно выходят обновления.
В качестве среды для разработки я выбрал Visual Studio Code. Для удобной сборки и запуска проекта был написан батник и добавлены горячие клавиши (но это тоже тема для отдельного поста). Так я собираю проект и запускаю его в эмуляторе нажатием одной клавиши на клавиатуре.
Глубоко закапываться в технические особенности консоли и разработки под нее я не хочу, так как ресурс все-таки развлекательный. Хочу отметить только несколько основных особенностей.
Вся графика состоит из тайлов 8х8 пикселей. При этом каждый тайл может использовать только 3 цвета и один прозрачный цвет (прозрачный цвет отображает цвет фона, а цвет фона определяется определенными битами регистров палитр).
Это значит, что требуется сводить все используемые спрайты к трем цветам, но если если спрайт состоит из нескольких тайлов, каждый тайл может использовать разные палитры (примерно так же это работает и для фона, но там немного сложнее).
Поэтому, если вы хотите вывести случайную картинку в формат NES, то ее нужно свести в формат 4 цветов и до нужно разрешения (256×240 пикселей). Вот пример преобразования картинки в формат, который уже можно вывести в вашу игру:
Реальное фото я преобразовал в денди формат (фото использовано с разрешения автора)
Подготовку изображения в почти автоматическом режиме легко сделать в фотошопе или гимпе (я использую гимп).
Но самая сложная часть разработки - это рисования пиксель арта в условиях очень низкого разрешения и ограниченности в количество цветов.
Для первых версий игры я использовал готовые спрайты из Front Mission без дополнительной подготовки. Они выглядели так себе, так как игра выходила на 16-битных консолях. Вот так это выглядело:
Спрайты меха в разных положениях
Когда я показывал людям эти спрайты и не уточнял, что это такое, большинство говорило, что похоже больше на месиво пикселей (я с этим согласен, меха там разглядеть сложно).
Но для тестов мне таких спрайтов хватало. И после рисования сетки поля я решил ее немного украсить травой, что в итоге породило вот такое кислотное чудо:
Скриншот режима битвы одной из ранних версий игры
На скриншоте выше видно, что трава действительно вышла даже немного симпатичная и в меню я тогда использовал некрасивые рамки.
Помимо режима битвы в игру был добавлен режим открытого мира. Карту мира я набросал довольно быстро и больше ее особо не переделывал. Выглядит она на данных момент вот так:
Временная карта мира
На карте мира каждая локация обозначена постройкой и войти в нее можно с помощью нажатия кнопки "A" на геймпаде. Локация в верхнем-левом углу - это локация-хаб с магазином, а остальные запускают сценарий битвы при входе (в каждой локации разный набор врагов, они отличаются комплектацией мехов и их количеством).
После того, как основные механики были реализованы, был начат процесс перерисовки мехов.
Как я уже говорил, одновременно можно использовать 3 цвета и один прозрачный цвет, который использует цвет фона. 3 цвета на при разрешении 16х24 пикселя на одно меха - это очень мало.
Поэтому я использовал решение, которое дало мне дополнительный цвет. Для этого я отказался от травы на экране и залил весь экран черным фоном. Это дало мне черный четвертый цвет для рисования мехов. Т.е. если я делаю прозрачный пиксель в спрайте, он выглядит как черный. Это позволило нарисовать более-менее адекватных мехов, которые выглядят как мехи. Вот так это выглядит в редакторе:
Спрайты мехов с использования прозрачных пикселей с черным фоном
Уже намного интереснее, но для анимации ходьбы нужно нарисовать для каждого положения по два спрайта (этим я пока не занимался, так как такой мелкий пиксельарт приходится рисовать буквально по пикселю, это очень муторно).
Многие технические ограничения консоли можно обойти за счет использования мапперов, но я пока обхожусь без него, так как хочу сделать создание реального картриджа максимально дешевым и простым. Стараюсь уложиться в стандартные 32 килобайта (место я потратил еще не все, музыка и прочее туда должно поместиться, тем более многое можно оптимизировать).
Резюмируя особенности разработки, можно сказать, что разработка игр для ретро-консолей особо ничем не отличается от разработки программ для микроконтроллеров. По этой причине постоянно приходится смотреть даташиты, описывающие разные узлы консоли.
Механики и ЛОР игры
Игра представляет собой классическую пошаговую стратегию, основанную на механике использования очков действия (AP). Каждое действие тратит очки действия, примерно как в лучшей в мире игре Fallout 2 (только у меня клетки квадратные). Система с фазами (как в X-COM) мне нравится меньше.
Панель, которая отображает состояние деталей меха и количество очков действия.
В режиме битвы игрок управляет своим мехом и пытается уничтожить вражеские мехи. Мех считается уничтоженным, если он лишается тела или обеих рук (без рук мех бесполезен). При этом, если мех лишается головы, он сильно теряет в точности, а если у него взрываются ноги, то он не может передвигаться. Битва заканчивается уничтожением всех вражеских мехов или гибелью игрока. Если игрок побеждает, он получает деньги за каждую сохраненную деталь вражеского меха.
Все оружие в игре делится на 4 типа:
Рукопашное оружие (может атаковать на одну клетку и есть возможность выбора точки атаки)
Пушки (наносят урон по случайной части меха с расстояния)
Снайперское оружие (наносят урон с расстояния и имеют возможность выбрать деталь вражеского меха для атаки)
Дробовики/картечницы (наносят урон по нескольким случайным частям меха)
Возможно будут еще какие-то типы оружия.
Вот пример меню выбора части меха для атаки:
Меню выбора части меха для нанесения по ней урона
Еще есть режим осмотра мехов (отображает имя, состояние частей меха и вооружение):
Режим осмотра меха, очки действия не тратит (Но может должно тратить?)
В режиме перемещения вы перемещаете меха курсором (каждый шаг тратит одно очко AP). Кнопка 'A' подтверждает перемещение, а кнопка 'B' возвращает меха на начальную точку (отменяет перемещение).
Механика использования экипировки пока не реализована.
Следующей важной частью игры является прокачка меха.
Вот так выглядит список доступных мест в локации-хабе:
Выбор места для посещения в хабе
А вот для примера магазин оружия:
Магазин оружия
Кнопка А покупает оружие в правую руку, В в левую, а Select возвращает вас на карту мира.
Вот мы и рассмотрели все основные особенности игры. Остальное вы можете попробовать сами используя эмулятор (ссылку на скачивание эмулятора и рома игры я дам в конце, если пикабу ссылки не уберет).
Планы по развитию игры
Планов по развитию игры у меня довольно много. Давайте приведу основные планы в виде списка:
Создание нормальной карты мира и проработка локаций на ней
Динамические анимации движения мехов в режиме битвы
Создание возможности ходить персонажем по хаб-локации и общаться там с людьми (но это сильно упирается в 32 килобайта памяти)
Написание лора с сюжетом и добавление его в игру
Создание диалоговой системы
Добавление звуков и музыки в игру (музыка на денди особенно специфическая штука)
Добавление новых спрайтов для мехов
Добавление различных построек и препятствий в режиме битвы (для оживления поля битвы)
Улучшение ИИ и добавление новых механик в битву
Расширение количества доступных частей и оружия для мехов
Режим игры для двоих игроков (PvP и PvE)
Для первого поста и анонса игры, я думаю, информации более-чем достаточно. Очень жду ваших предложений и замечаний по игре. Весь этот текст был написан в первую очередь ради получения обратной связи от потенциальных игроков, так как я не могу знать, что нравится другим людям, а исходить только из собственных соображений - это путь в никуда.
Поэтому жду вас в комментариях, готов ответить на все ваши вопросы. Спасибо за внимание.
Список основных ресурсов, которые я использовал при разработке:
Цикл статей по разработке игры для денди - https://habr.com/ru/articles/348022/
Википедия по разработке для NES (там все оч подробно описано и с примерами кода, но английским) - https://www.nesdev.org/wiki/NES_reference_guide
Живой форум по разработке ретро-игр (и не только) - emu-land.net
Сайт компилятор СС65 - https://www.cc65.org/
Еще несколько хороших статей про устройство консоли (на русском) - http://dendy.migera.ru/nes/g00.html
Эмулятор который я использую для отладки игры - fceux.com
Страница проекта. Там можно скачать ROM-файл для эмулятора (Нужно ли делать отдельный паблик для игры?) - http://73-it.ru/the-iron-steam.html
Видео-версия статьи с геймплеем и моими комментариям
Ассемблер бесподобен
Взято из телеграмма - Инкогнито
С чего начать изучать программирования?
В школе на уроках информатиках нас ничему не учат кроме всяких кругов Эйлера,поэтому я решил сам начать изучать языки программировпния.Посоветуйте,пж,с чего начать?Сначала хотел бы научиться создавать сайты без редакторов.