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

в 19:44, , рубрики: diy или сделай сам, esp8266 arduino, программирование микроконтроллеров, Разработка под Arduino

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 тысяч операций в секунду. Можно использовать разные цветные экраны, есть софтовое растягивание изображения до нужных пропорций.

Виртуальная машина на ESP8266 для запуска игр - 2
Некоторые игры, из написанных мной, можно посмотреть тут.

Одновременно с 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 можно почитать туториал. Спасибо за внимание.

Автор: corax89

Источник

* - обязательные к заполнению поля