Jump to content

    

Движок для исполнения пользовательского скрипта.

1 hour ago, xvr said:

В атаче в моём предыдущем посте готовый проект (вместе с всеми необходимыми для VS файлами)

 

Проект - С++ Win32 Console

 

я когда делаю Build получаю


Error    MSB6006    "cmd.exe" exited with code 9009.   

я правда сделал Retarget Solution, у меня VS2017 но более старая версия SDK.

 

Share this post


Link to post
Share on other sites

Посмотрите в окне build output (или как оно там называется), что пишет. Должно быть больше, чем просто Error    MSB6006    "cmd.exe" exited with code 9009.   

Share this post


Link to post
Share on other sites
2 hours ago, xvr said:

Посмотрите в окне build output (или как оно там называется), что пишет. Должно быть больше, чем просто Error    MSB6006    "cmd.exe" exited with code 9009.   

только это.

Severity    Code      Description                                Project   File                                                                                                                                                                  Line    Suppression State
Error    MSB6006    "cmd.exe" exited with code 9009.    script    C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets    171    

а строка 171 - <CustomBuild

 

а вот

Quote

1>------ Build started: Project: script, Configuration: Debug x64 ------
1>'bison' is not recognized as an internal or external command,
1>operable program or batch file.
1>Performing Custom Build Tools
1>C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets(171,5): error MSB6006: "cmd.exe" exited with code 9009.
1>Done building project "script.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

bison не находит

Edited by jenya7

Share this post


Link to post
Share on other sites
1 hour ago, jenya7 said:

bison не находит

Угу. Он у вас должен быть в путях. cygwin стоит?

Откройте окно с переменными окружения (для Win10: знак Win внизу слева -> параметры (шестерёнка) -> Система -> О системе -> Сопутствующие параметры/сведения о системе -> Дополнительные параметры системы -> Дополнительно -> Переменные среды -> В 'системных переменных' выбираете Path -> Изменить). Дописываете вниз путь к папке bin внутри cygwin

 

Перезапустить VS

Share this post


Link to post
Share on other sites
1 hour ago, xvr said:

Угу. Он у вас должен быть в путях. cygwin стоит?

Откройте окно с переменными окружения (для Win10: знак Win внизу слева -> параметры (шестерёнка) -> Система -> О системе -> Сопутствующие параметры/сведения о системе -> Дополнительные параметры системы -> Дополнительно -> Переменные среды -> В 'системных переменных' выбираете Path -> Изменить). Дописываете вниз путь к папке bin внутри cygwin

 

Перезапустить VS

а что именно в cygwin запустить. у меня инсталировалось куча экзешников но ни одного визуального не нахожу.

Share this post


Link to post
Share on other sites
20 minutes ago, jenya7 said:

а что именно в cygwin запустить

В нём ничего запускать не надо - это коллекция утилит командной строки (в том числе и bison)

 

Вы при инсталяции самого cygwin'а пакет с bison выбрали?

 

Share this post


Link to post
Share on other sites
16 hours ago, xvr said:

В нём ничего запускать не надо - это коллекция утилит командной строки (в том числе и bison)

 

Вы при инсталяции самого cygwin'а пакет с bison выбрали?

 

не знаю. инсталировал все по умолчанию.

Share this post


Link to post
Share on other sites
2 hours ago, jenya7 said:

не знаю. инсталировал все по умолчанию.

По умолчанию он вообще ничего не ставит. Запустите инсталятор ещё раз, когда дойдёте до страницы 'select packages' в поле search набейте bison, в списке внизу выберите последюю версию пакета bison (в столбце New) и нажимайте 'Дальше'

 

Share this post


Link to post
Share on other sites

Последняя часть - интерпретатор

Spoiler

#include <stdint.h>
#include <stdlib.h>

#include "opcodes.h"

static int32_t* vars[] = {
#define VAR(nm) &nm,
#include "var_defs.inc"
 NULL
};

// External interface
extern uint8_t get_byte(void); // Read 1 byte of script
extern void skip_script(uint16_t); // Skip required amount of script bytes

// Stack manupulation
static int32_t stack[32];
static int32_t *sp; // 'Top of stack' pointer

// push & pop value from stack. No stack overflow/underflow
static void push(int32_t value) {*sp++ = value;}
static int32_t pop(void) {return *--sp;}


// Some wrappers to get 2/4 bytes from script storage
static int16_t get_word(void)
{
    uint8_t tmp = get_byte();
    return tmp | (get_byte() << 8);
}

static int32_t get_dword(void)
{
    uint16_t tmp = get_word();
    return tmp | (get_word() << 16);
}

void interpreter(void)
{
    for(;;)
    {
        int32_t left, right;
        uint8_t opcode = get_byte();
        if (opcode == OP_Stop) return;
        if ( (opcode&MASK_Const0) == OP_Const0)
        {
            int32_t value = opcode;
            if (opcode&0x40) value |=0xFFFFFFC0;
            push(value);
            continue;
        }
        if ( (opcode&MASK_Var) != MASK_Var) // This is one of var access opcodes
        {
            uint16_t var_idx = opcode & 31;
            if (var_idx == 31) var_idx = get_byte() + 31;
            switch(opcode)
            {
                case OP_ReadVar: push(*vars[var_idx]); break;
                case OP_SetVar: *vars[var_idx] = pop(); break;
                case OP_AddVar: *vars[var_idx] += pop(); break;
            }
            continue;
        }
        switch(opcode)
        {
            case OP_Const2: push(get_word()); continue;
            case OP_Const4: push(get_dword()); continue;
            case OP_JmpF1: case OP_JmpF2:
                            {
                                uint16_t shift = opcode == OP_JmpF1 ? get_byte() : get_word(); 
                                if (!pop()) skip_script(shift);
                            }
                            continue;
            case OP_Neg:    push(-pop()); continue;
        }
        left = pop();
        right = pop();
        switch(opcode)
        {
            case OP_EQ:    left = left == right; break;
            case OP_NE:    left = left != right; break;
            case OP_GT:    left = left >  right; break;
            case OP_GE:    left = left >= right; break;
            case OP_And:   left = left && right; break;
            case OP_Or:    left = left || right; break;
            case OP_Add:   left = left +  right; break;
            case OP_Sub:   left = left -  right; break;
            case OP_Mul:   left = left *  right; break;
            case OP_Div:   left = right ? left / right : left < 0 ? INT32_MIN : left > 0 ? INT32_MAX : 0; break;
            case OP_Mod:   left = right ? left % right : 0; break;
        }
        push(left);
    }
}

 

Предполагается, что все внешние переменные скрипта видимы на уровне файла, их имена на уровне С совпадают с тем, как задано в файла var_defs.inc и они все 32х битные

Т.е. если у вас в скрипте есть переменная PWM1, то и в С можно написать PWM1 и это и будет та самая переменная

 

 

Share this post


Link to post
Share on other sites
1 hour ago, xvr said:

Последняя часть - интерпретатор

  Hide contents


#include <stdint.h>
#include <stdlib.h>

#include "opcodes.h"

static int32_t* vars[] = {
#define VAR(nm) &nm,
#include "var_defs.inc"
 NULL
};

// External interface
extern uint8_t get_byte(void); // Read 1 byte of script
extern void skip_script(uint16_t); // Skip required amount of script bytes

// Stack manupulation
static int32_t stack[32];
static int32_t *sp; // 'Top of stack' pointer

// push & pop value from stack. No stack overflow/underflow
static void push(int32_t value) {*sp++ = value;}
static int32_t pop(void) {return *--sp;}


// Some wrappers to get 2/4 bytes from script storage
static int16_t get_word(void)
{
    uint8_t tmp = get_byte();
    return tmp | (get_byte() << 8);
}

static int32_t get_dword(void)
{
    uint16_t tmp = get_word();
    return tmp | (get_word() << 16);
}

void interpreter(void)
{
    for(;;)
    {
        int32_t left, right;
        uint8_t opcode = get_byte();
        if (opcode == OP_Stop) return;
        if ( (opcode&MASK_Const0) == OP_Const0)
        {
            int32_t value = opcode;
            if (opcode&0x40) value |=0xFFFFFFC0;
            push(value);
            continue;
        }
        if ( (opcode&MASK_Var) != MASK_Var) // This is one of var access opcodes
        {
            uint16_t var_idx = opcode & 31;
            if (var_idx == 31) var_idx = get_byte() + 31;
            switch(opcode)
            {
                case OP_ReadVar: push(*vars[var_idx]); break;
                case OP_SetVar: *vars[var_idx] = pop(); break;
                case OP_AddVar: *vars[var_idx] += pop(); break;
            }
            continue;
        }
        switch(opcode)
        {
            case OP_Const2: push(get_word()); continue;
            case OP_Const4: push(get_dword()); continue;
            case OP_JmpF1: case OP_JmpF2:
                            {
                                uint16_t shift = opcode == OP_JmpF1 ? get_byte() : get_word(); 
                                if (!pop()) skip_script(shift);
                            }
                            continue;
            case OP_Neg:    push(-pop()); continue;
        }
        left = pop();
        right = pop();
        switch(opcode)
        {
            case OP_EQ:    left = left == right; break;
            case OP_NE:    left = left != right; break;
            case OP_GT:    left = left >  right; break;
            case OP_GE:    left = left >= right; break;
            case OP_And:   left = left && right; break;
            case OP_Or:    left = left || right; break;
            case OP_Add:   left = left +  right; break;
            case OP_Sub:   left = left -  right; break;
            case OP_Mul:   left = left *  right; break;
            case OP_Div:   left = right ? left / right : left < 0 ? INT32_MIN : left > 0 ? INT32_MAX : 0; break;
            case OP_Mod:   left = right ? left % right : 0; break;
        }
        push(left);
    }
}

 

Предполагается, что все внешние переменные скрипта видимы на уровне файла, их имена на уровне С совпадают с тем, как задано в файла var_defs.inc и они все 32х битные

Т.е. если у вас в скрипте есть переменная PWM1, то и в С можно написать PWM1 и это и будет та самая переменная

 

 

а extern uint8_t get_byte(void); откуда берем?

Share this post


Link to post
Share on other sites
18 minutes ago, jenya7 said:

а extern uint8_t get_byte(void); откуда берем?

Вы должны предоставить (и skip_script тоже)

 

Реализация может быть любая - например читать побайтно скрипт из внешней FLASH (или где он у вас находится)

Реализация skip_script в самом простом виде может быть такая -

void skip_script(uint16_t length)
{
	while(length--) get_byte();
}

Но если скрипт читается снаружи блоками, то скип можно сделать гораздо более эффективно

Share this post


Link to post
Share on other sites

доинсталировал бизоновский пакет, прописал путь, все равно не видит бизон.

я другого не понимаю - главное это интерпретер, он делает всю работу. какая разница каким способом распарсить и передать ему опкоды?

Share this post


Link to post
Share on other sites

Раз уж тут никого не смущает бизон и запуск интерпретации на стороне ПК, то может быть просто воспользоваться LLVM? ))))

 

Share this post


Link to post
Share on other sites
3 hours ago, sigmaN said:

Раз уж тут никого не смущает бизон и запуск интерпретации на стороне ПК, то может быть просто воспользоваться LLVM? ))))

 

интерпритатор как раз таки нужно запускать на микроконтролере.

Share this post


Link to post
Share on other sites
12 hours ago, jenya7 said:

интерпритатор как раз таки нужно запускать на микроконтролере.

Это понятно. Из LLVM можно взять фронтэнд какого-то языка, оптимизацию и написать только интерпретатор для микроконтроллера и бэкэнд для LLVM.

Есть еще одна мысль:
Раз уж запуск каких-то компиляторов на стороне ПК это не проблема, то почему бы не использовать С/С++ компилятор для вашего микроконтроллера для превращения вашего "скрипта" в нативные инструкции?
Ваши "скрипты" превращается в функции. Их будет конечное кол-во(с заранее заданными именами). Основная прошивка будет их вызывать в нужных местах, передавая в качестве первого параметра указатель на некое глобальное состояние(структуру),
с помощью которой ваш скрипт может взаимодействовать с основной прошивкой.
Дальше можно по-фантазировать... Либо заранее зарезервировать под каждый такой скрипт определенное имя и кол-во байт, либо из основной прошивки за скриптом обращаться к некому менеджеру, который по запросу передаст выполнение нужному скрипту(кодировка скриптов по значению int переменной), тогда снимаются ограничения на кол-во и размер каждого из таких "скриптов".

Из плюсов такого нативного подхода:
+Стандартный, всем известный и понятный язык программирования
+Возможность отделить защищенную основную прошивку от этих доп.модулей(скриптов)
+Нативный код, все оптимизации нативного компилятора

Минусы
-Возможные проблемы с безопасностью. Надо думать, как ограничивать этому куску нативного кода доступ к памяти, чтобы он не с дампил вашу основную прошивку злоумышленникам. Есть ли вообще такая проблема и задача у вас?
Наверно этот вопрос может быть решен путем анализа области памяти со "скриптами" и выявления там инструкций, которые пытаются получить доступ к чему-то запрещенному (запрещенный диапазон адресов например). Так можно ограничить доступ и к оборудованию и к памяти... Но, это вопрос дискуссионный. Сходу, без серьезного анализа, за безопасность такого решения я бы отвечать не стал )) Но, вроде как, это довольно не сложный анализ должен быть(со списком запрещенных инструкций). Скрипт, заподозренный в чем-то не хорошем, просто помечается как опасный и из основной прошивки управление ему не передаётся. Надо еще как-то хитро ограничить доступ к запрещенным областям памяти по указателям....
В общем, над безопасностью нужно хорошо подумать )

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now