Виртуальная машина на ESP8266 для запуска игр

    VM, написанная неуверенной рукой гуманитария в среде программирования Arduino с использованием быдлокода и велосипедов. А еще есть компилятор для нее из си-подобного языка, написанный на JavaScript теми же методами. Да. Уже можно спешить в комментарии, бросать камни. Ну а тех, кому все же интересно, приглашаю продолжить чтение.

    Троллейбус из буханки

    В общем-то мою поделку уже немного осветил уважаемый tormozedison вот тут. Но на тот момент был скорее сферический в вакууме прототип. А сейчас у меня появилось устройство работы RomanS, которое он любезно предоставил мне для опытов совершенно безвозмездно. Зовется сие устройство ESPboy. От остальных поделок оно отличается компактностью и слотом расширения, использующим микросхему MCP23017. Тут о нем можно узнать больше.

    Если кому интересно, как мне вообще пришла в голову такая странная мысль, как виртуальная машина на микроконтроллере, то можно прочитать под спойлером.

    Предыстория
    Когда-то в школе я увлекся гейммэкером от йойогеймс, и, как многие, клепал на нем не пойми что. Этими криво написанными и плохо нарисованными демками еще можно было похвастаться друзьям, но в интернете они тонули среди похожих. Я понял, как тяжело самому написать даже простую игру, не говоря уже о серьезной, в которой можно было бы грабить корованы. Но желание писать игры не пропало. Когда у меня появился мобильный телефон, я открыл для себя Midlet Pascal и несколько лет развлекался им. Ведь я понимал, что написать игру для слабого устройства проще, чем для его более мощного большого брата. По крайней мере, всегда можно было бы говорить не о кривых руках, а о ограниченных возможностях платформы. Однако кнопочники сменились сенсорами очередной шанс завоевать мир был упущен. Все же, в какой-то момент я понял, что могу возродить свои мечты, используя микроконтроллер.

    Начал я с написания игры под ардуино уно, куда же без нее. Тогда она у меня была единственная, и я очень волновался за ее работоспособность. Старался прошивать как можно реже. Но путь граблей идется маленькими шажками. Каждая небольшая правка кода заставляла прошивать снова и снова. И я решил написать стековую виртуальную машину. Как-никак на борту огромные два килобайта оперативной памяти для байт кода и данных. Но какой же она была медленной, а памяти почему то постоянно не хватало. Наверное, все дело в том, что все мои велосипеды были с квадратными колесами и медленно ехали. Тогда я решил написать эмулятор chip-8 на только что пришедшую из Китая ардуино мега. Конечно же, потом я хотел написать для нее же еще и эмулятор геймбоя, первой плейстейшн и какого-нибудь суперкомпьютера попроще. В результате я понял главное. Виртуальная машина chip-8 была очень простой, чрезмерно простой. Настолько, что при написании своих игр для умножения или деления приходилось писать отдельную медленную подпрограмму. А значит нужно написать свою VM, избавившись от фатального недостатка. В это время еще и несколько esp8266 приехало, со своими космическими 160MHz.

    Если вам кажется, что виртуальная машина это пустая трата ресурсов, то я еще и переписал под нее свой эмулятор chip-8. Получилась виртуальная машина в виртуальной машине на микроконтроллере. Наверное, можно пойти дальше, и написать машину тьюринга на chip-8.

    Технические характеристики ESP Little Game Engine


    Виртуальная машина содержит 16 регистров по 16 бит, нулевой регистр является указателем стека. Каждая инструкция двухбайтовая, некоторые инструкции содержат после себя два байта данных. Адресуемая память 64KB. В случае ESP8266 доступно 20KB. Возможна загрузка программы из SPIFFS и UART. При желании можно добавить загрузка с карты памяти или через WiFi. Кроме обычных арифметических инструкций и инструкций перемещения данных, есть отдельные инструкции для работы со спрайтами, экраном и звуком. Размер экрана 128 на 128 пикселей. При 16 цветах на точку экран занимает 8KB памяти, еще столько же занимает буфер для рисования спрайтов и частиц. Хоть используемая мной библиотека TFT_eSPI способна обновлять экран более 60 раз в секунду, пришлось ограничиться 20 кадрами в секунду. Иначе не хватало процессорного времени для виртуальной машины. Можно рисовать тайлы и 32 спрайта размером до 128х128 пикселей с возможностью вращения и зеркалирования. Для экономии памяти можно использовать однобитные изображения или RLE сжатие. Присутствует упрощенная физика: обнаружение столкновений спрайтов со спрайтами и тайлами, разрешение столкновений, гравитация. Экран обновляется построчно, только если в строке произошло изменение пикселей. Скорость VM в зависимости от того, сколько строк отрисовывается в кадре, варьируется от 100 тысяч до 900 тысяч операций в секунду. Можно использовать разные цветные экраны, есть софтовое растягивание изображения до нужных пропорций.


    Некоторые игры, из написанных мной, можно посмотреть тут.

    Одновременно с VM для ESP8266 я писал эмулятор на JavaScript для браузера. Что бы не редактировать байт-код вручную, был добавлен простенький ассемблер, основанный на моем опыте с ассемблером для MOS6502. Затем я решил добавить язык более высокого уровня. Все же моей основной задачей было быстрое написание простых игр, а не долгая отладка ассемблерного кода. Мне показалось, что написать свой компилятор будет проще, чем добавить LLVM. И я написал его на JavaScript, так как знаю его не так плохо, как другие языки. На данный момент ему далеко до поддержки стандартов C и при компиляции можно легко столкнуться с непонятной ошибкой в непонятном месте. Зато он быстр, ведь он занимает меньше 2000 строк.

    А теперь к вопросу, зачем я вообще все это здесь написал. Уже сейчас можно писать и запускать игры. Однако до совершенства еще далеко. Если кто захотел мне помочь в доработке ESP LGE, или написать свою собственную игру, то я буду очень рад. Если Вам моя идея показалась интересной, и Вы хотите узнать больше, то я с радостью отвечу на интересующие вопросы. Предупреждаю, код для Arduino довольно сложно читать. Отчасти из-за того, что я самоучка. Отчасти из-за того, что я попытался уменьшить количество вызовов функций, для увеличения скорости работы. В результате многие функции содержат огромные портянки кода. Так что не подпускайте к экрану беременных и детей. Постепенно я постараюсь это исправить и улучшить читаемость.

    А для тех, кто дочитал, пример игры. Занимает меньше ста строк и меньше 1KB в скомпилированном виде.

    int stickCount;
    char key,previouseKey,takenSticks;
    
    void redraw(){
    	int i;
    	//выбираем красный цвет
    	setcolor(2);
    	//рисуем видимые палочки
    	for(i = 0; i < stickCount; i++)
    		line(22 + i * 6, 74, 22 + i * 6, 84);
    	//выбираем серый цвет
    	setcolor(11);
    	//рисуем выброшенные
    	for(i = stickCount; i < 15; i++)
    		line(22 + i * 6, 74, 22 + i * 6, 84);
    	//возвращаем белый цвет как основной
    	setcolor(1);
    	//ждем перерисовки экрана
    	delayredraw();
    }
    
    void playersMove(){
    	//если код кнопки равен предыдущему, значит она еще не отпущена. Ждем
    	while(key == previouseKey){
    		key = getkey();
    	}
    	while(key != KEY_LEFT && key != KEY_DOWN && key != KEY_RIGHT){
    		key = getkey();
    	}
    	if(key & KEY_LEFT){
    		takenSticks = 1;
    	}else if(key & KEY_DOWN){
    		takenSticks = 2;
    	}else{
    		takenSticks = 3;
    	}
    	printf("%d, ", takenSticks);
    	stickCount -= takenSticks;
    	previouseKey = key;
    }
    
    void computersMove(){
    	if(stickCount % 4){
    		//компьютер реализует выигрышную стратегию, если выпала возможность
    		takenSticks = stickCount % 4;
    	}else{
    		//компьютер ждет возможности реализовать выигрышную стратегию
    		takenSticks = 1 + random(1);
    	}
    	stickCount -= takenSticks;
    	printf("%d, ", takenSticks);
    }
    
    void game(){
    	//инициализация
    	stickCount = 15;
    	clearscreen();
    	//переводим каретку на восьмой символ нулевой строки
    	gotoxy(8,0);
    	puts("Баше");
    	gotoxy(2,1);
    	puts("Возьмите 1,2 или 3 палочки. Проигрывает тот,  кому   нечего  брать. Управление:\n");
    	//коды 27,25 и 26 соответствуют стрелкам
    	printf(" %c 1    %c 2    %c 3", 27, 25, 26);
    	gotoxy(0,12);
    	redraw();
    	while(1){
    		playersMove();
    		if(stickCount <= 0){
    			gotoxy(3,8);
    			puts("Вы выиграли");
    			return;
    		}
    		redraw();
    		computersMove();
    		redraw();
    		if(stickCount <= 0){
    			gotoxy(3,8);
    			puts("Компьютер выиграл");
    			return;
    		}
    	}
    }
    
    void main(){
    	while(1){
    		game();
    		//ждем секунду
    		settimer(1,1000);
    		while(gettimer(1)){}
    		while(getkey() == 0){}
    		previouseKey = key;
    	}
    }
    

    Можно сразу и протестировать. Перейдите по ссылке, затем нажмите compile, затем run. Если хотите узнать больше о возможностях IDE можно почитать туториал. Спасибо за внимание.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 23

      0

      Интересно, есть ли способы программировать esp, кроме arduino и lua?

        +1
        Наверное самый правильный, это использование SDK от производителя, есть еще и неофициальное SDK. В нем можно отключить много лишнего, получив дополнительную свободную память. Ну и питон имеется, и бейсик. Еще можно просто собрать прошивку в онлайн конструкторе из готовых компонентов.
          +1
          micropython ;)
            0
            freertos наше все… особенно под esp32
              0

              А можете что-нибудь посоветовать человеку, который умеет писать программы на чистом си для avr микроконтроллеров? Изучать lua?
              Или использовать Arduino IDE, которая для мигания светодиодом использует много килобайт?

                0

                А почему собственно не lua? Дока по языку даже меньше чем Dive into Python.

                  0
                  Если вы умеете писать на Си, то логично на нем и писать.
                  SDK от Espressif — на Си.
                  Arduino — это IDE. Что мешает писать код, который не требует много памяти для мигания светодиодом?
                  Или вы имеете в виду память ПК?
                  Можно использовать Eclipse с SDK от Espressif- только на ПК он еще больше памяти отъедает и подтормаживает.
                  Мне больше нравится использовать Platformio. Он поудобнее чем Eclipse и Arduino, но тут смотря кто к чему привык.
                    +1
                    Пишите в Arduino IDE на чистом Си без библиотек Wiring
                    Ведь что такое Arduino IDE? Посредственный редактор, GCC компилятор, загрузчик (под разные МК) и куча разных библиотек.
                    Например, для ESP8266 можно в IDE вызывать нативные функции ESP SDK
                    А Wiring хорош для переноса кода с одного МК на другой.
                      0
                      Почему не писать на чистом си? особенно если умешь. хотя луа учится быстро tylerneylon.com/a/learn-lua
                        +1
                        Arduino IDE ещё божески (для avr). Посмотрите для сравнения сколько весит бинарник blink.ino для ESP8266. Ну и что? Если памяти вагон…
                          0

                          Я привык к 8 килобайтами памяти на avr. И такое расходование памяти Arduino IDE мне не кажется правильным.

                            0
                            Можно использовать platformio вместо Arduino IDE, больше настроек, больше контроля и есть совместимость с Arduino IDE. Однако в таком большом размере бинарника в случае esp8266 виноваты огромные библиотеки Espressif, которые при каждой сборке линкуются к коду.
                      0

                      espruino на js`e

                      –7
                      offtopic
                      Чтобы сделать троллейбус понадобятся тролль и бусы )
                      imageimage
                        –1
                        Злые вы, серьезные, никакого чувства юмора.)
                        0

                        Два цикла while ввели меня в ступор…
                        Кстати, переменная key содержит один код клавиши или маску?

                          0
                          Функция getkey() возвращает байт, в котором каждый бит соответствует текущему состоянию восьми кнопок. Несколько циклов while как раз таки результат текущей недоработанности. Следует добавить прерывание по нажатию. Сейчас прерывания доступны только для событий в спрайтах (столкновение, выход за границы экрана).
                          0
                          Змейка какая-то бесконечная. Так не должно же быть.
                            +1
                            Почти все игры написаны за несколько часов, больше для примера. Так что у любой могут оказаться недостатки. Но она не такая и бесконечная, рано или поздно змейка станет слишком длинной и съест свой хвост.
                            0
                            Если честно, я ничего не понял.

                            Виртуальная машина эмулирует что, Спектрум, 8086, Gameboy, БЭСМ, что-то другое? Приведенный выше код чем компилируется и чем выполняется? Какие игры запускаются, от какой платформы?

                            Автору, если уж вы описываете свое творение, пишите с учётом того что человек ваш проект видит первый раз в жизни.
                              0
                              Ничего не эмулирует. Выполняет собственный байткод. Компилируется онлайн компилятором, ссылка в последнем абзаце. Игры свои собственные запускает.
                                0
                                Спасибо, значит я с этим перепутал — github.com/lualiliu/esp32-gameboy

                                Ну, для самообразования и так сойдет :) Описали бы подробнее архитектуру, структуру байткода и пр, чтобы это могло и другим пригодиться.
                                  0
                                  Спасибо за совет. Пожалуй так и сделаю.

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое