Разработка скетчей на языке BrainFuck
- Войдите на сайт для отправки комментариев
Данный материал предназначен для «настоящих программистов» , которые, как известно, «не используют Паскаль». Это не значит, что читать его могут только профессионалы. К целевой аудитории относятся и новички, но только те из них, кто в душе «настоящие программисты» и набор необходимых навыков для них лишь вопрос времени.
Итак, поехали!
Почему-то исторически так сложилось, что скетчи для Ардуино разрабатываются основном на С и С++. Есть конечно, отдельные личности, ратующие за язык ассемблера и небольшая группа сектантов, что-то мямлящих про Паскаль, но общей картины это не меняет – подавляющее большинство программистов для Ардуино - С’онисты. Это факт и спорить с ним бессмысленно.
И факт этот не может не вызывать сожаления у «настоящих программистов». Ведь в мире существует такое множество прекрасных языков, на которых можно писать замечательные скетчи! Чего, стоит только великий язык INTERCAL с его операторами COMEFROM («получить управление из» - полная противоположность GOTO – шутка разработчиков языка на тему GOTO-фобии), циклами «делать пока» и «не делать пока» и, разумеется, замечательным модификатором PLEASE, позволяющим сделать компилятор и среду исполнения более сговорчивыми. А взять язык, WHITESPACE! Это же не программирование, а поэзия! Вся Ваша программа целиком состоит из пробелов, табуляция и переводов строки! Представляете, как эффектно она выгладит напечатанной на бумаге! По крайней мере, ни одна зараза не сделает Вам замечания, что Вы как-то не так расположили отступы!
Но даже в ряду этих прекрасных языков, есть жемчужины, которые просто не с чем сравнивать. И одной из таких жемчужин, безусловно, является язык программирования BrainFuck ( статья в Википедии со множеством ссылок, группа русскоязычных фанатов языка в ЖЖ ). Название можно перевести на русский язык как «Вынос мозга» и поверьте, язык своё название отрабатывает сполна!
Ресурсы об этом языке легко гуглятся и, если Вы копнёте, то увидите, что он очень популярен – по всему миру проходят конкурсы по написанию программ на нём, много сред разработки, реализации под все платформы и т.д. Особенно в восторге это этого языка будут новички, поскольку в нём практически отсутствует синтаксис, а стало быть, невозможны синтаксические ошибки, сообщениями о которых С-компилятор выносит новичкам мозг почище всякого BrainFuck’а (справедливости ради, замечу, что одну ошибку там таки можно сделать, но об этом позже).
Итак, что же это за язык?
Программы исполняются на т.н. BrainFuck-машине, которая имеет упорядоченный массив памяти, состоящий из ячеек изначально заполненных нулями. Классически ячейка представляется байтом, но есть реализации и с двух и даже четырёхбайтовыми ячейками. Сам язык не накладывает никаких ограничений. У машины имеется головка считывания/записи, которая всегда находится над какой-нибудь ячейкой, которую называют «текущая ячейка». Ну, и, наконец, имеется 8 команд:
> | сдвинуть головку на 1 ячейку вправо |
< | сдвинуть головку на 1 ячейку влево |
+ | увеличить содержимое текущей ячейки на 1 |
- | уменьшить содержимое текущей ячейки на 1 |
. | вывести содержимое текущей ячейки в поток вывода |
, | ввести символ из потока ввода и поместить его в текущую ячейку |
[ | если в текущей ячейке 0, то пропустить кусок программы, до парной ] |
] | если в текущей ячейке не 0, то вернуться назад к парной [ и продолжать выполнение оттуда |
Это всё! Больше в языке нет ничего!
Синтаксис программы очень простой. Команды пишутся последовательно и отделяются друг от друга чем угодно (включая и «ничем»). Любые другие символы (не являющиеся командами) считаются разделителями и просто игнорируются. Это открывает широкий простор для комментирования программ, хотя настоящим программистам комментарии не нужны, т.к. ничто не документирует код лучше, чем сам код :)
Примеры правильных программ на BrainFuck
>>.<++,- < [+,<>] >>>этот текст ни на что не влияет, просто разделитель >>>+++.---<
Единственная возможность допустить синтаксическую ошибку – не соблюсти баланс квадратных скобок. Единственная возможная ошибка времени выполнения – выход за границы памяти. Если не допускать этих двух ошибок, то любая последовательность любых символов является разумной программой и как-нибудь да выполнится . Представляете, какие возможности открываются перед творческими людьми? :)
Несмотря на скромное количество команд, язык является алгоритмически полным (при условии бесконечного размера памяти, конечно), а это означает, что на нём можно записать любой алгоритм. Доказательством этого факта я здесь заморачиваться не буду, просто поверьте, что это так. Таким образом, снимаются всякие вопросы о том, а можно ли на нём сделать «то или это». Можно всё, что представимо в виде алгоритма. Заметим, кстати, что горячо любимый С++ тоже алгоритмически полон при условии бесконечной памяти.
Теперь, когда мы познакомились со столь замечательным языком, надеюсь, никто не станет спорить с тем, что сегодня великий день для всего Ардуино – сообщества, т.к. именно сегодня BrainFuck становится доступным для разработки Ардуино-скетчей! Да, да, друзья мои! Я счастлив представить вам первый, простой, но уже вполне функциональный интерпретатор, с помощью которого мы можем писать скетчи на BrainFuck прямо в привычной нам среде Arduino IDE. Уверен, что появление такого замечательного языка в нашем «ящике с инструментами» произведет революцию в написании скетчей! Никогда ещё разрабатывать их не было так легко и приятно, и никогда ещё скетчи не были столь простыми и понятными!
Особенности BrainFuck-машины для Arduino
Несмотря на многочисленные соблазны, было принято решение не дополнять язык никакими Ардуино-ориентированными расширениями и сохранить его в первозданной чистоте. Специфика платформы отражена только в структуре памяти BrainFuck-машины. К ячейкам общего назначения добавлены специальные ячейки, соответствующие портам ввода/вывода Ардуино. В данной реализации интерпретатора поддерживаются двенадцать портов – цифровые пины со второго по тринадцатый включительно. Всего имеется 512 ячеек общего назначения и 12 ячеек-портов. Изначально головка установлена над нулевой ячейкой. Справа от неё остальные 511 ячейки памяти, а слева 12 ячеек соответствующих пинам. Номера пинов растут справа налево.
Поддержка аналоговых пинов, таймеров и всего остального оставлена на будущие версии интерпретатора. Пока у нас есть только цифровые пины.
Перед началом исполнения программы, ячейки общего назначения содержат нули (как и определено в языке), а вот ячейки-порты соответствуют реальному состоянию портов Ардуино: содержат нули, если на соответствующем пине LOW и единицы, если на соответствующем пине HIGH.
Операции «.» и «,» для ячеек общего назначения означают вывод в Serial и ввод оттуда соответственно (ввод сделан без проверки, а есть ли там что-то, просто выполняется read()). Эти же операции для ячеек-портов означают вывод содержимого текущей ячейки в порт (digitalWrite) и ввод состояния порта в текущую ячейку (digitalRead).
Интерпретатор реализован в виде библиотеки, содержащей всего одну функцию BrainFuck с единственным параметром типа char *. Параметр – собственно программа на BrainFuck (в виде строки), которую необходимо выполнить. Функция возвращает:
BF_SUCCESS - если программа выполнилась успешно
BF_PROGRAM_UNDERFLOW – если отсутствует открывающая [
BF_PROGRAM_EXHAUSTED – если отсутствует закрывающая ]
BF_MEMORY_UNDERFLOW – если операция > вышла за пределы памяти
BF_MEMORY_EXHAUSTED – если операция < вышла за пределы памяти
Константы определены в файле BrainFuck.h
Для того, чтобы всё заработало необходимо установить библиотеку. Для этого в папке libraries нужно создать папку BrainFuck и поместить в неё два файла: BrainFuck.cpp и BrainFuck.h
Текст файла BrainFuck.cpp
#include <arduino.h> #include <string.h> #include "brainfuck.h" #define PINS_MEMORY_SIZE 12 #define DB_MEMORY_SIZE (DB_MEMORY + PINS_MEMORY_SIZE) BFResult BrainFuck(char * bfProgram) { static char bfMemory[DB_MEMORY_SIZE]; int memPtr = PINS_MEMORY_SIZE; int loopCounter = 0; // // Инициализация памяти memset(bfMemory + PINS_MEMORY_SIZE, 0, DB_MEMORY); for (int8_t i = 2; i < 14; i++) bfMemory[PINS_MEMORY_SIZE + 1 - i] = digitalRead(i); // // удаление из строки-программы всех символов кроме <>+-.,[] static const char * bfCommands = "<>+-.,[]"; char *p = bfProgram; for (int ncmd = strspn(p, bfCommands); ncmd < (int)strlen(p); ncmd = strspn(p, bfCommands)) { p += ncmd; strcpy(p, p + strcspn(p, bfCommands)); } // // Собственно цикл интерпретатора const int programSize = strlen(bfProgram); for (int progPtr = 0; progPtr < programSize; progPtr++) { switch (bfProgram[progPtr]) { case '>': memPtr++; break; case '<': memPtr--; break; case '+': bfMemory[memPtr]++; break; case '-': bfMemory[memPtr]--; break; case '.': if (memPtr < PINS_MEMORY_SIZE) digitalWrite(PINS_MEMORY_SIZE + 1 - memPtr, bfMemory[memPtr]); else Serial.print(bfMemory[memPtr]); break; case ',': if (memPtr < PINS_MEMORY_SIZE) bfMemory[memPtr] = digitalRead(PINS_MEMORY_SIZE + 1 - memPtr); else bfMemory[memPtr] = Serial.read(); break; case '[': if (!bfMemory[memPtr]) { loopCounter++; while (loopCounter) { if (++progPtr >= programSize) return BF_PROGRAM_EXHAUSTED; if (bfProgram[progPtr] == '[') loopCounter++; else if (bfProgram[progPtr] == ']') loopCounter--; } } break; case ']': if (bfMemory[memPtr]) { loopCounter++; while (loopCounter) { if (--progPtr < 0) return BF_PROGRAM_UNDERFLOW; if (bfProgram[progPtr] == '[') loopCounter--; else if (bfProgram[progPtr] == ']') loopCounter++; } if (--progPtr < -1) return BF_PROGRAM_UNDERFLOW; } break; } if (memPtr < 0) return BF_MEMORY_UNDERFLOW; if (memPtr >= DB_MEMORY_SIZE) return BF_MEMORY_EXHAUSTED; } return BF_SUCCESS; }
Текст файла BrainFuck.h
#ifndef BRAINFUCK_H #define BRAINFUCK_H #define DB_MEMORY 512 typedef enum { BF_SUCCESS = 0, BF_PROGRAM_UNDERFLOW, BF_PROGRAM_EXHAUSTED, BF_MEMORY_UNDERFLOW, BF_MEMORY_EXHAUSTED } BFResult; // // ВНИМАНИЕ!!! // Функция изменяет переданную ей строку-аргумент // а именно удаляет из неё все символы, не являющиеся // командами языка BrainFuck // BFResult BrainFuck(char *); #endif // BRAINFUCK_H
Вот собственно и всё. Добро пожаловать в славную семью брейнфакеров!
Как это работает?
Прежде, чем перейти к полномасштабным примерам, рассмотрим простейшие приёмы программирования. Основные команды уже были приведены выше, сейчас же посмотрим как с их помощью делать элементарные операции.
1. вывести в Serial 0 (не символ ‘0’, а именно число 0)
Поскольку изначально в ячейках нули, то нужно просто вывести текущую ячейку операцией «.»
#include <BrainFuck.h> void setup(void) { Serial.begin(115200); BrainFuck("."); } void loop(void) {}
2. вывести в Serial символ ‘0’
Поскольку изначально в ячейках нули, а код символа ‘0’ – 48, то можно 48 раз увеличить текущую ячейку на 1, а затем вывести её значение операцией «.» (это простейший способ, но программу можно сделать гораздо короче, если воспользоваться циклом)
#include <BrainFuck.h> void setup(void) { Serial.begin(115200); BrainFuck("++++++++++++++++++++++++++++++++++++++++++++++++."); } void loop(void) {}
Можете запустить и полюбоваться на нолик в мониторе порта.
3. Включить светодиод на пине 13
Изначально головка стоит напротив ячейки 0 (см. рисунок выше). Чтобы перевести её к ячейке соответствующей пину 13, необходимо сдвинуть её влево на 12 позиций. Затем можно увеличить содержимое текущей ячейки на 1 и вывести её в порт операцией «.». Если светодиод перед этим не горел (в ячейке был 0), то он загорится. Если же горел (в ячейке была 1), то он будет продолжать гореть.
#include <BrainFuck.h> void setup(void) { BrainFuck("<<<<<<<<<<<<+."); } void loop(void) {}
Любуйтесь.
Примеры программ
Теперь перейдём к примерам более или менее реальных программ.
Обычно принято начинать с примера «Hello, World!». В Ардуино роль «Hello, World!» играет «Blink» и правильно было бы начать с него, но из большого уважения к сообществу С’онистов, которые скоро могут официально стать ажно целым этносом, я всё-таки начну с классического «Hello, World!», тем более, что мне это ничего не стоит, т.к. программу я попросту стащил с Википедии ничего в ней не меняя.
«Hello, World!»
Программа выводит в Serial фразу «Hello, World!». Комментировать я её не буду, т.к. она буквально посимвольно разобрана в Википедии, кому интересно, смотрите подробный разбор там.
#include <BrainFuck.h> /////////////////////////////////////////////////////////// // // Программа на BrainFuck // Выводит в Serial строку "Hello, World!" // static char * bfHelloWorld = (char *) "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++" ".>+.+++++++..+++.>++.<<+++++++++++++++.>.+++." "------.--------.>+.>."; void setup(void) { Serial.begin(115200); BrainFuck(bfHelloWorld); } void loop(void) {}
Обратите внимание, как лаконично, строго и законченно выглядит текст на BrainFuck! Такая простота, лаконичность и ясность просто недостижима в языках типа С, не правда ли?
«Blink»
Теперь, когда долг исполнен и мир поприветствован, давайте помигаем светодиодом – это ведь наше любимое занятие, не правда ли? Задача разбивается на две: инвертировать светодиод и подождать задержку. Если это сделать, то можно вставлять всё это в loop и радоваться.
Давайте думать, как это сделать. У меня вот такая идея по поводу инвертирования:
1) ячейку 0 используем для индикации нужно или нет зажигать светодиод. Сначала поместим туда единицу (типа, нужно).
2) пойдём к ячейке 13-го пина и если в ней 0 (светодиод не горит), то ничего делать пока не будем, а вот если 1 (светодиод горит), то погасим его и обнулим ячейку 0
3) теперь идём к ячейке 0 и, если в ней не 0 (светодиод нужно зажигать), то зажигаем его.
Что касается задержки, то отработаем её циклами. Одним не получится, т.к. в нашу однобайтовую ячейку счётчик более, чем на 255 итераций не поместится, но сделаем несколько вложенных. Сколько нужно циклов подберём эмпирически.
Вот что получилось (видит Бог, комментирование заняло втрое больше времени, чем написание):
#include <BrainFuck.h> /////////////////////////////////////////////////////////// // // Программа на BrainFuck // Обыкновенный Blink с частотой приблизительно 1Гц // программу нужно просто вставить в loop // static char * bfBlink = (char *) "" // //Инвертируем пин 13 // "+" // в ячейку №0 пока запишем 1, впредь там будет 1, если светодиод надо зажечь и 0 если нет "<<<<<<<<<<<<" // топаем к ячейке 13го пина "[" // ЕСЛИ там не 0 (на пине HIGH) ТО "-" // делаем его нулём "." // и выводим 0 (LOW) на пин ">>>>>>>>>>>>" // топаем обратно к ячейке №0 "[-]" // обуляем ячейку №0 (это признак того, что светодиод зажигать не надо) "<<<<<<<<<<<<" // возвращаемся обратно и пину 13 "[-]" // обнуляем текущую ячейку, чтобы выйти из цикла "]" // выход из цикла (этим циклом мы смоделировали IF) // // К этому моменту головка стоит на ячейке пина 13 // В ячейке №0 находится 1, если светодиод надо зажигать // или 0, если светодиод зажигать не надо // ">>>>>>>>>>>>" // топаем к ячейке №0 "[" // ЕСЛИ там не 0 (надо зажигать светодиод) ТО "<<<<<<<<<<<<" // топаем к ячейке 13го пина "+" // увеличиваем её на 1 (теперь там 1) "." // записываем 1 (HIGH) в порт "[-]" // обнуляем текущую ячейку чтобы выйти из цикла "]" // выход из цикла (этим циклом мы смоделировали IF) // // К этому пин 13 нивертирован Если там было HIGH то стало LOW и наоборот // // Теперь нужно отработать задержку // Поскольку у нас нет длоступа к слубам времени и темаерам // будем отрабатывать задержку просто циклом // // Сделаем 5 вложенных циклов Эмпирически подобрано так чтобы // частота блинка была 1Гц // "+++++++" // Увеличиваем текущую ячеку на 7 "[" // ЕСЛИ там не 0 ТО ">++++++++" // переходим к соседней (вправо) ячейке и увеличиваем её на 8 "[" // ЕСЛИ там не 0 ТО ">++++++++" // переходим к соседней (вправо) ячейке и увеличиваем её на 8 "[" // ЕСЛИ там не 0 ТО ">++++++++" // переходим к соседней (вправо) ячейке и увеличиваем её на 8 "[" // ЕСЛИ там не 0 ТО ">++++++++" // переходим к соседней (вправо) ячейке и увеличиваем её на 8 "[-]" // обнуляем текущую ячейку чтобы выйти из цикла "<-" // возвращаемся в ячейку на шаг влево и уменьшаем её на 1 "]" // конец цикла "<-" // возвращаемся в ячейку на шаг влево и уменьшаем её на 1 "]" // конец цикла "<-" // возвращаемся в ячейку на шаг влево и уменьшаем её на 1 "]" // конец цикла "<-" // возвращаемся в ячейку на шаг влево и уменьшаем её на 1 "]"; // конец цикла void setup(void) { pinMode(13, OUTPUT); } void loop(void) { BrainFuck(bfBlink); }
В принципе, это программа работает (запустите и проверьте), но вот выглядит она совсем не по BrainFuck’овски. С точки зрения идейно-выдержанного BrainFuck’а это и не программа вовсе, а просто «шозанах» какой-то! Правильная, идейно-выдержанная программа на BrainFuck должна выглядеть вот так:
#include <BrainFuck.h> /////////////////////////////////////////////////////////// // // Программа на BrainFuck // Обыкновенный Blink с частотой прибл. 1гц // программу нужно просто вставить в loop // static char * bfBlink = (char *) "+<<<<<<<<<<<<[-.>>>>>>>>>>>>[-]<<<<<<<<<<<<[-]]" ">>>>>>>>>>>>[<<<<<<<<<<<<+.[-]]+++++++[>+++++++" "+[>++++++++[>++++++++[>++++++++[-]<-]<-]<-]<-]"; void setup(void) { pinMode(13, OUTPUT); } void loop(void) { BrainFuck(bfBlink); }
Делает она тоже самое, что и предыдущая, но выглядит так, как и должны выглядеть программы на BrainFuck написанные настоящими программистами!
Предыдущий пример всем хорош, но он требует, чтобы программа на BrainFuck вызывалась из loop() и у случайного читателя (не у настоящего программиста, конечно) может возникнуть подозрение, что нам слабо организовать бесконечный цикл на самом BrainFuck. Спешу разочаровать – таки нет, не слабо! Более того, легко. Смотрите.
#include <BrainFuck.h> /////////////////////////////////////////////////////////// // // Программа на BrainFuck // Модификация Blink с частотой прибл. 1Гц // не нуждается во внешнем цикле loop // её достаточно вставить в setup - сама будет // мигать, т.к. цикл в ней уже есть свой // static char * bfBlinkL = (char *) "+[>" // Внешний цикл "+<<<<<<<<<<<<<[-.>>>>>>>>>>>>>[-]<<<<<<<<<<<<<[-" // Эти две строки инвертируют "]]>>>>>>>>>>>>>[<<<<<<<<<<<<<+.>>>>>>>>>>>>>[-]]" // цифровой пин 13 "+++++++[>++++++++[>++++++++[>++++++++[>++++++++[" // Эти две строки дают задержку "-]<-]<-]<-]<-]" // для блинка "<]"; // Конец внешнего цикла void setup(void) { pinMode(13, OUTPUT); BrainFuck(bfBlinkL); } void loop(void) {}
Цикл здесь прокомментирован. В остальном пример точно такой же, как и предыдущий, надеюсь, разобраться в нём не составит труда.
Ну, и последний на сегодня пример – обработка нажатия кнопки. Допустим, к 12-му пину подключена кнопка. Пин притянут к земле и при нажатии кнопки на нём появляется HIGH. Задача – зажигать светодиод на 13-ом пине тогда, когда кнопка нажата и гасить, когда не нажата. Комментировать пример я не стал – оставляю читателю самому разобраться. Пример вполне работоспособный, можете загрузить и попробовать.
#include <BrainFuck.h> /////////////////////////////////////////////////////////// // // Программа на BrainFuck // Зажигает светодион на пине 13, если на пине 12 HIGH // и гасит, если там LOW. Нужно вставить в loop() // static char * bfButton = (char *) "+<<<<<<<<<<<[<+.>>>>>>>>>>>>[-]<<<<<<<<<<<[-" "]]>>>>>>>>>>>[<<<<<<<<<<<<[-].>>>>>>>>>>>>[-]]"; void setup(void) { pinMode(13, OUTPUT); pinMode(12, INPUT); } void loop(void) { BrainFuck(bfButton); }
Ну, вот как-то так коллеги, прошу так сказать любить и жаловать новый (для ардуинистов) язык программирования.
Конеыно, интерпретатор неполностью поддерживает платформу и нуждается в расширении (аналоговые пины, служба времени, прерывания и т.п.). Также не помешал ьы и компилятор непосредственно в коды AVR. Ну, и может быть аппаратная реализация BrainFuck-машины, но это уже выходит за рамки Ардуино. Но всё это потом. И только в том случае, если язык наберёт достаточное количество сторонников в чём я лично, учитывая необычайную простоту, красоту и выразительную мощь этого языка, нисколько не сомневаюсь!
С языком я познакомился в 2000 году. Я тогда преподавал, и случилась история, описание которой я позже прочитал на Лурке. Видит Бог, я не знаком ни с кем с Лурка и потому, думаю, что я не единственный преподаватель, с кем случилась такая история – попса, в общем.
Дал я студентам задание и предложил сделать его на любом, удобном языке программирования. Одной из моих целей было посмотреть, как они будут выбирать язык. И вот нашёлся брэйнфакер, который притащил мне программу на этом самом языке.
Ну, я погуглил, нашёл описание языка и подумал: «Ага, парень, шутить изволишь? Ну, так я тоже пошучу!». Зарылся в его программу, нашел ошибки и спокойно, с самой невинной рожей выслал ему входные данные, при которых программа валится с предложением исправить ошибку. Потом второй раз, потом третий и так возил его мордой об стол полсеместра пока он уже не отладился настолько, что я больше не сумел найти ошибок. Тогда принял.
Парень сейчас на очень хорошей позиции разработчика в Intel. В прошлом году встречались и вспоминали эту историю с кучей шуток и улыбок.
Кстати, прошу прощения, ссылка "группа русскоязычных фанатов языка в ЖЖ" в первом посте почему-то битая. Правильная ссылка - http://ru_brainfucker.livejournal.com/
Празднуем ровно месяц как исполнилось первое апреля?
Да, что Вы! Заботимся о сообществе! Разнообразим инструментарий :)
А вообще. не знаю, что я праздную, но настроение хорошее, вот потянуло на что-нибудь эдакое :)
Надо бы замутить конкуср среди новичков на что-нибудь вроде "самый короткий блинк на брейнфаке" или типа того. Готов поучаствовать в формировании призового фонда.
Уже очередь! Того гляди и сервер ляжет от наплыва.... ;)
Браво! Давно так не ржал.. реализовать интерпретатор для дунек .. только почему BrainFuck, когда это почти чистый Тьюринг?
Ну и у Вас там должна быть небольшая ошибка, связанная с 13 пином: его "прочитать" практически нельзя. На нем висит встроенный светодиод и чтение с него надо обходить как "специальный случай".
Ещё раз спасибки.
только почему BrainFuck, когда это почти чистый Тьюринг?
Бог его знает, почему его Мюллер (автор языка) так назвал, я с ним не знаком.
у и у Вас там должна быть небольшая ошибка, связанная с 13 пином: его "прочитать" практически нельзя. На нем висит встроенный светодиод и чтение с него надо обходить как "специальный случай".
Вот этого я не понял. Наверное, мы о разных вещах говорим. Блинк же из примера в этой статье работает. Да и вот такой блинк тоже вполне работает
Так что, нормально он читается. Вы, наверное что-то другое имели в виду?
Ещё раз спасибки.
Не за что, я рад, что Вас улыбнуло. Собственно и писал чтобы люди поулыбались.
И чем чуваку цифры не понравились? )) Он наверное специально задумал Побрейнфакать всем мозг
Если б были числа в нём был бы нормальный язык, а так просто ужас, бесполезный ужас, ну разве что мозг потренировать, только не программировать на нём
Занятно... :)
Только какие плюсы получим от этого "мозготраха" ??? скорость??? размер???
Никаких. Язык чисто сакральный. Чувак прикололся. Это как одежда от кутюр. Предназначена только для показа на подиуме. Кстати это ещё не самый дибильный из языков, вот https://ru.wikipedia.org/wiki/Категория:Эзотерические_языки_программирования сами выбирайте чемпиона.
Ребята, ну BrainFuck, конечно, шутка. Но это из тех самых шуток, которые, как говорят, содержат долю шутки. Это юмор программистов. Я сейчас имею в виду не тех людей, которые умеют писать коды, а профессиональных программистов, которые занимаются, в том числе, теорией и фундаментальными основами программирования.
Работы над созданием минимального языка стали очень актуальными с тех пор как (1970-ые) было показано, что программы «сотканные» из небольшого числа примитивных инструкций получаются отпимальнее, чем из большого набора сложных инструкций. Это был теоретический результат, практическим выхлопом которого стала, например RISC архитектура (привет, Arduino!). Также побочной ветвью эти поисков оказались транспьютеры, да и много чего другого.
Но в науке не бывает так, чтобы без широких теоретических поисков вдруг взяла и сразу появилась RISC архитектура. В процессе этих самых поисков порождались такие странные конструкции, как, например, процессоры без единой команды вообще (ZISC - Zero Instruction Set Computer). Также есть многочисленные процессоры с одной единственной командой (OISC – One Instruction Set Computer). Без этих теоретических работ не было бы и RISC.
Также эти исследования породили массу холиваров по поводу расположения программы и данных – в единой памяти или в различных. Сегодня параллельно живут оба подхода. В единой (например, Intel), и в разных (например, AVR). Вот, к примеру, процессор BrainFuck предполагает раздельное хранение программы и данных. Из-за этого в нём невозможны самомодифицирующиеся программы. А вот, замечательный язык BitBitJump, в котором всего одна команда (OISC архитектура) предполагает хранение программы и данных в единой памяти и. соответственно поощряет самомодификацию (без неё там нихрена не напишешь, на самом деле). И какую, Вы думаете, программу автор этого языка написал на нём одной из первых? Вы не поверите, но он написал интерпретатор BrainFuck’а! Вот здесь сборник материалов по языку BitBitJump и в т.ч. текст интерпретатора BrainFuck’а на нём.
Иногда из таких научных работ появляются очень специфические игры для программистов. Классический пример: «Core War» (по русски). Это игра, которая проходит на специализированном компьютере с памятью замкнутой в виде кольца и небольшим набором команд (в первой версии было 9 команд, потом разрослось до 18). Язык напоминает привычный ассемблер. Программы и данные живут в одной памяти. Изначально программы располагаются в случайных адресах. Программа погибает, когда пытается исполнить нечто, не являющееся правильной командой. Задача играющего – убить программу – противника, т.е. найти её и записать в её тело неправильную инструкцию, чтобы та её выполнила. Игру придумал профессор Александр Дьюдни из Канады.
(приятное отступление о триумфе нашего программирования ::)))
(конец приятного отступление о триумфе нашего программирования ::)))
Т.е., всё о чём написано в этом топике, это серьёзная большая наука, но в ней работают люди, у них тоже есть чувство юмора, и иногда из их научных занятий рождаются шутки типа BrainFunck’а или CoreWar. Если Вам кажется, что это несерьёзно и типа «люди фигнёй страдают», Вы просто не понимаете этих работ. Не понимаете не потому, что с Вами что-то не так, но просто – мы много чего не понимаем в «чужих профессиях». Помните, как говорил Козьма Петрович Прутков:
«Многие вещи нам непонятны не потому, что наши понятия слабы; но потому, что сии вещи не входят в круг наших понятий».
Только какие плюсы получим от этого "мозготраха" ??? скорость??? размер???
Такие языки появляются в недрах теоретических работ и нужны для этих работ (см. мой пост выше). Для практики от них ПРЯМОГО толку немного, но опосредованно - польза огромная. Например, появление RISC-архитектуры (не ловите меня на слове, она появилась раньше этого языка, просто она вышла из тех же теоретических работ, что и этот язык).
От только академического наукообразия здесь нехватало. Те теор. работы - прикладная математика, к програмированию относится так же, как математика к любой другой сфере деятельности, что-то можна расчитать, где-то сплошная эмпирика а где-то вобще безполезна. RISC архитектура тут вобще никаким боком, разве что идея что команд надо мало общая, но она слишком общая. Програмирование не наука, а ремесло для сообразительных. По способности к решению головоломок легко определить получится програмист или нет, сколько не учи.
А этот язык - в топку, он бесполезен, а это главный критерий.
От только академического наукообразия здесь нехватало. Те теор. работы - прикладная математика, к програмированию относится так же, как математика к любой другой сфере деятельности, что-то можна расчитать, где-то сплошная эмпирика а где-то вобще безполезна. RISC архитектура тут вобще никаким боком, разве что идея что команд надо мало общая, но она слишком общая. Програмирование не наука, а ремесло для сообразительных. По способности к решению головоломок легко определить получится програмист или нет, сколько не учи.
«Многие вещи нам непонятны не потому, что наши понятия слабы; но потому, что сии вещи не входят в круг наших понятий» (К. Прутков)
"Болтун подобен маятнику: того и другой надо остановить. " (К. Прутков)
"Всякая человеческая голова подобна желудку: одна переваривает входящую в оную пищу, а другая от неё засоряется." (К. Прутков)
Там много, нет предмета который нельзя подтвердит или опровергнуть афоризмами.
А этот язык - в топку, он бесполезен, а это главный критерий.
Практическая полезность не может быть главным критерием, поскольку этот критерий близорук. Есть масса полезных вещей, унитаз, например, но из них уже ничего не вырастет. А из совершенно бесполезного, заумного и теоретического результата - теоремы Робинсона выросла современная теория компиляции, результаты которой мы "кушаем", как свинья "кушала жёлуди под дубом".
Логик, вот я Вас не понимаю. Топик написан как шутка, чтобы люди улынулись. Вам обязательно выливать сюда ведро желчи и устраивать холивар? Идите Вы нафиг. Не нравится - не читай.
А авторы компиляторов то и не знают...
А авторы компиляторов то и не знают...
Знают. В отличие от Вас.
Логик, вот я Вас не понимаю. Топик написан как шутка, чтобы люди улынулись. Вам обязательно выливать сюда ведро желчи и устраивать холивар? Идите Вы нафиг. Не нравится - не читай.
Читаю - чё хочу, и пишу аналогично. Шуткой это сразу было, пока Вы не завели речи о научной пользе этой хрени. Её нет. Докажите обратное, если не трепло. Прогу на этой фигне напишите, опобликуйте, тогда о пользе можно говорить, а натягивать сову на глобус не надо, мы тут не студенты а Вы не препод. Согласно кивать и ржать над тупостью из субординации не обязаны. Вы забываетесь где Вы.
Прогу на этой фигне напишите, опобликуйте,
Я прог не пишу, это не ко мне, обратитесь к прогерам.
А вообще мы говорим о разных вещах. Вертеть баранку может любой дегенерат, а автомобиль создавали гении. Так вот я говорю о людях, которые делают программирование, а Вы о прогерах, которые кодят проги - это разные люди.
А вот мне было интересно почитать "академическое наукообразие", плюсую. Может быть потому, что сам на работе часто общаюсь с преподами: профессорами, кандидатами наук, доцентами. Знаком с процессом академических изысканий. Правда кафедры экономики и финансов, но всё равно с такими людьми интересно общаться. Особенно, когда оказалось, что один препод по маркетингу и финансам является кандидатом технических наук, доцент. Да ещё и радиолюбитель, имеет свой трансивер Yaesu с личным позывным.
P.S. Простите за оффтоп.
Евген ты бы лучше парочку Уроков написал как на Atmel Studio кодить, а то на русском нет нифига толком или как прогать для Attiny2313 в Arduino IDE
Евген ты бы лучше парочку Уроков написал как на Atmel Studio кодить, а то на русском нет нифига толком или как прогать для Attiny2313 в Arduino IDE
Я не умею ни кодить, ни прогать :(
Тут есть специалисты, попросите их.
Евген ты бы лучше парочку Уроков написал как на Atmel Studio кодить, а то на русском нет нифига толком или как прогать для Attiny2313 в Arduino IDE
А чем кодинг в Atmel Studio отличается от кодинга в Arduino IDE? Тот же Си, только нет специфичных ардуиновских библиотечных функций и т.п. Язык тот же, синтаксис тот же. Ну да, в Atmel Studio нет таких высокоуровневых абстракций, как в Arduino IDE. Нужно разбираться с конкретным микроконтроллером на более низком уровне - переферия, регистры и т.д., читать даташиты и умные статьи (статей на русском кстати полно).
Тот же не тотже а примеры нужны как вообще там кодить, уроки на конткретных примерах, тогда легко втягиваешься без заморочек, а не самотыком заниматься (комьютерным ононизмом)
По моему там тоже есть ООП если не ошибаюсь только что бибилов Ардуновских нет, но ими там можно пользоваться также.
Там несколько вариантов проектов. Надо ООП - создавай проект С++, не надо - создавай проект С или вообще Assembler.
Как кодить в Atmel Studio:
1. Создаём проект
2. Выбираем микроконтроллер
3. Получаем пустую заготовку вида:
4. Кодим
5. PROFIT!
Как непосредственно прошить МК из под студии я описывал ЗДЕСЬ.
Вот Вам несколько ссылок на статьи по кодингу AVR:
http://easyelectronics.ru/category/avr-uchebnyj-kurs
http://www.doneathome.ru/archives/category/%D1%83%D1%80%D0%BE%D0%BA%D0%B8
http://chipenable.ru/index.php/programming-avr.html
http://alex-exe.ru/category/radio/avr/
http://cxem.net/mc/mc.php
http://forum.cxem.net/?showtopic=136229
http://microtechnics.ru/category/mikrokontrollery/avr/
http://microsin.net/programming/avr/index.html
http://robocraft.ru/tag/AVR/
http://makesystem.net/?cat=52
И видео:
https://www.youtube.com/playlist?list=PLBLtydguylgDUtd9qRAt82u-pUa9x6O8Y
https://www.youtube.com/playlist?list=PLjOP2VNyr7kQ77mBQujwLqyBYf98xMUMO
https://www.youtube.com/playlist?list=PLygUYOEl6XIqkHSWQVtczrlhOLja6kdi-
https://www.youtube.com/playlist?list=PLygUYOEl6XIpFyd-1n6O2RYCqLisJl-W6
https://www.youtube.com/playlist?list=PLygUYOEl6XIrGEzxbg1qsitAa3oekoUJ6
https://www.youtube.com/playlist?list=PLygUYOEl6XIp6rz5e8EfWJUoJ6VJ2MEzF
P.S. Прошу прощения у ЕвгенияП за то, что засрали тему оффтопом. Надо бы куда-то это всё перенести...
А что именно определяет вид контроллера? Только Заливку? Можно ли писать для разных контроллеров прогу?
По хорошему вообще нужно ВСе темы с уроками и прочими отдельными поучительными статьями Организовать в Отдельтные КАТЕГОРИИ форума наряду с Программирование и прочее
А то всё разбросано непонять как по форуму, информацию нерально искать
А что именно определяет вид контроллера? Только Заливку?
Нет. В разных микроконтроллерах может быть разная переферия и регистры. Вам придётся углубиться в изучение архитектуры AVR.
Можно ли писать для разных контроллеров прогу?
Можно. Но с учётом особенностей конкретного микроконтроллера. Я же писал выше, что в Atmel Studio нет такой абстракции, как в Arduino IDE.
Евген ты бы лучше парочку Уроков написал как на Atmel Studio кодить, а то на русском нет нифига толком или как прогать для Attiny2313 в Arduino IDE
Я не умею ни кодить, ни прогать :(
Тут есть специалисты, попросите их.
Ну тогда по Алгоритмам и какие лучше способы хранения данных выбирать, например вот надо хранить кучу Таймеров и Будильников, в каком виде это делать? применительно к Ардуине конечно
что производительней будет Массив структур? или лучше всё все поля сделать отдельными массивами (с одинаковыми ID = iй член массива)? или стеком (если честно пока не знаю что это такое) Если например надо это всё на TFT отрисовывать оперативно
А есть ли вариант напрямую с ОЗУ работать как с EEPROM не нарушая работы контроллера и программы? насколько это производительней будет?
Ну то есть с помощью IFDEF (define делая отдельные участки кода для разных контроллеров можно ?
А можно заливать Проект из студии не через студию а левыми отладчиками через другие среды? ассемблерный код она не выдает, чтоб напрямую лить потом?
А то всё разбросано непонять как по форуму, информацию нерально искать
А вы ищите не по этому форуму, а по всему интернету. Не стесняйтесь спрашивать у Гугла, он много знает ;-)
Да дело то в том что неизвестно как правильно поисковый запрос составить для конкретного вопроса, если пишешь в общем то муть всякая, а конктретно не знаешь чего писать, тк сам в этом ещё не разбираешься (в том что хочешь узнать)
Чёт, ты, кажись, в лужу пукнул. А каже на нём sck читается при работе программатора? Или при SPI в slave режиме? Да и digitalRead его отлично читает. Может он в arhat.h не читается? Так это к автору либы вопросы, а не к пину.
Ну то есть с помощью IFDEF (define делая отдельные участки кода для разных контроллеров можно ?
Да, такое практикуется.
А можно заливать Проект из студии не через студию а левыми отладчиками через другие среды? ассемблерный код она не выдает, чтоб напрямую лить потом?
Студия на выхлопе даёт hex, elf, заливайте чем хотите.
Вот этого я не понял. Наверное, мы о разных вещах говорим. Блинк же из примера в этой статье работает. Да и вот такой блинк тоже вполне работает
Так что, нормально он читается. Вы, наверное что-то другое имели в виду?
Нет, я как раз про этот вариант. У меня на моей меге2560 такой вот точно не работает. Наступал не раз, и приходится вводить переменную, в которой отслеживать состояние пина. Странно, что у Вас пример работает.
По остальной части дискусии: Да, как пример реализации машины Тьюринга, очень даже наукоемкий язык и из таких изысканий в теории языков выросло много чего. Только вот замечу, что Атмеловские AVR-ы - ни разу не RISC "по определению". Первый, полноценный RISC это .. PDP-11, как ни странно. Боюсь ошибиться, но тут Reduced Instruction даже не "пахнет". Да и второе положение RISC-ов сильно недотягивает: равноправность РОН (регистровая, безаккумуляторная арифметика), которая как раз и призвана обеспечить эту "reduced".
В моем понимании RISC - это немного однотактовых команд, с большим количеством РОН (16-32-..), за счет чего имеющие упрощенную логику и способные обходится и вовсе без Блока Микропрограммного Управления (БМУ) для "перекодриовки команд". А тут .. как жеж без БМУ-то? Самый обыкновенный CISC, да ещё и с кривым набором - ибо функциональная неполнота:
1. Наличие "аккумулятора" в ряде команд в r0,r1.
2. Несимметрия косвенной адресации, в частности EICALL есть только для регистра Z (r30,r31).
3. Неполнота команд арифметики. Инкремент для байтов есть, а для пар регистров - нету .. ну и по мелочи такие же несуразицы..
4. Неполнота методов доступа в память, особенно flash, eeprom. Про последнюю, ладно .. можно считать "периферией".
Забавляет то, что этот кривой набор команд в рекламных слоганах подается как достоинство: "оптимизированный набор команд". :)
А в чем разница? Втыкаете первой строкой #include "arhat.h" и пишете хоть на коленке, пардон в блокноте .. точно также как и прежде в ИДЕ. Атмел студию не смотрел, но не вижу причин почему бы оно так не работало. Те же самые digitalWrite() ..
Нет, я как раз про этот вариант. У меня на моей меге2560 такой вот точно не работает. Наступал не раз, и приходится вводить переменную, в которой отслеживать состояние пина. Странно, что у Вас пример работает.
Странно. Может у Вас конкретный экземпляр Меги дефектный? Вроде это стандартный приём, я его во многих местах видел.
Атмеловские AVR-ы - ни разу не RISC "по определению".
Ну, чёткого, точного, позволяющего проверку определения не существует в природе :)))) Все имеющиеся определения скорее оценочные. А вот коллеги из Atmel с Вами не согласны. Они везде пишут про "Advanced RISC Architecture" :)))
Но, как говаривали герои фильма "Особенности национальной рыбалки": "О терминологии мы спорить не будем" :)))
О, не обращал внимания .. так у них жеж "Advanced" .. как понимаю в сторону CISCов.. :)
Ребята, у кого есть мега, проверьте пожалуйста, действительно ли код
на меге не пашет или это у Архата мега неправильная попалась?
На ATmega328 (и на Uno c DIP, и на Nano) работает за милую душу.
Вопрос-то непраздный, хотелось бы знать.
Спасибо, если кто посмотрит и отпишется.
Ну у меня не пашет - точно. Проверено не единожды, а ещё с этой ноги не идут прерывания PCINT. Тоже интересно, это она только у меня такая или это так и должно быть?
Это шутка такая ?
Ну, в каждой шутке, как известно ...
По крайней мере, это реально работает, можете запустить и проверить. А уж fuck свой brain или не fuck - каждый сам решает :)))
На Mega 2560 работает однозначно.
Может данную тему убрать из закреплённых? А то ЕвгенийП ещё несколько этюдов напишет и вся первая страница раздела Программирование из закреплённых тем будет состоять. Или завести новый раздел "Закрепленные темы" (вместо Processing например)?
У меня работает, был код
так вот он работает, но скажите мне почему следующий код не работает?
я уже как то с этим сталкивался и тож думал что не работает, начал проверять оказалось что не работает 2й код а первый работает, кто скажет почему ?
Ребята, у кого есть мега, проверьте пожалуйста, действительно ли код
на меге не пашет или это у Архата мега неправильная попалась?
На ATmega328 (и на Uno c DIP, и на Nano) работает за милую душу.
Вопрос-то непраздный, хотелось бы знать.
Спасибо, если кто посмотрит и отпишется.
Сейчас я на работе, проверить смогу, когда домой доберусь.
А как у Вас сконфигурированы пины?
Зачем эта тема в закрепленных находится?
А вот прошу прощения, щас проверл и уже все работает, видимо какая то ошибка была в коде УРА