Часть 26: Создаем свой язык программирования для Arduino

Что, если вы не хотите использовать C++ или ассемблер? Что, если вы хотите создать свой собственный язык, который будет идеально подходить для ваших задач? Мы создадим интерпретатор байт-кода, который будет выполняться на ATmega328P.

Архитектура виртуальной машины:

// Байт-код виртуальной машины
#define OP_NOP      0x00
#define OP_LOAD     0x01  // Загрузить константу
#define OP_STORE    0x02  // Сохранить в переменную
#define OP_ADD      0x03  // Сложение
#define OP_SUB      0x04  // Вычитание
#define OP_MUL      0x05  // Умножение
#define OP_DIV      0x06  // Деление
#define OP_CMP      0x07  // Сравнение
#define OP_JMP      0x08  // Безусловный переход
#define OP_JZ       0x09  // Переход если ноль
#define OP_CALL     0x0A  // Вызов функции
#define OP_RET      0x0B  // Возврат
#define OP_PRINT    0x0C  // Вывод
#define OP_READ     0x0D  // Чтение с пина
#define OP_WRITE    0x0E  // Запись на пин
#define OP_DELAY    0x0F  // Задержка

// Виртуальная машина
class VirtualMachine {
private:
    uint8_t memory[256];      // 256 байт оперативной памяти
    uint8_t regs[16];         // 16 регистров
    uint16_t pc = 0;          // Счетчик команд
    uint8_t sp = 0;           // Указатель стека
    uint8_t stack[16];        // Стек вызовов
    bool running = true;
    
public:
    void loadProgram(const uint8_t* program, uint16_t size) {
        memcpy(memory, program, size);
    }
    
    void execute() {
        while (running) {
            uint8_t opcode = memory[pc++];
            
            switch (opcode) {
                case OP_LOAD: {
                    uint8_t reg = memory[pc++];
                    uint8_t value = memory[pc++];
                    regs[reg] = value;
                    break;
                }
                case OP_ADD: {
                    uint8_t reg1 = memory[pc++];
                    uint8_t reg2 = memory[pc++];
                    uint8_t dest = memory[pc++];
                    regs[dest] = regs[reg1] + regs[reg2];
                    break;
                }
                case OP_PRINT: {
                    uint8_t reg = memory[pc++];
                    Serial.println(regs[reg]);
                    break;
                }
                case OP_WRITE: {
                    uint8_t pin = memory[pc++];
                    uint8_t reg = memory[pc++];
                    digitalWrite(pin, regs[reg]);
                    break;
                }
                case OP_READ: {
                    uint8_t pin = memory[pc++];
                    uint8_t reg = memory[pc++];
                    regs[reg] = digitalRead(pin);
                    break;
                }
                case OP_DELAY: {
                    uint8_t reg = memory[pc++];
                    delay(regs[reg]);
                    break;
                }
                case OP_JMP: {
                    uint16_t addr = memory[pc] | (memory[pc+1] << 8);
                    pc = addr;
                    break;
                }
                case OP_JZ: {
                    uint8_t reg = memory[pc++];
                    uint16_t addr = memory[pc] | (memory[pc+1] << 8);
                    if (regs[reg] == 0) {
                        pc = addr;
                    }
                    break;
                }
                case OP_HALT:
                    running = false;
                    break;
            }
        }
    }
};

// Пример программы на нашем языке (мигаем LED)
const uint8_t blinkProgram[] = {
    OP_LOAD, 0, 1,         // reg0 = 1
    OP_LOAD, 1, 13,        // reg1 = 13 (пин)
    OP_WRITE, 1, 0,        // digitalWrite(13, reg0)
    OP_DELAY, 0,           // delay(reg0)
    OP_LOAD, 0, 0,         // reg0 = 0
    OP_WRITE, 1, 0,        // digitalWrite(13, 0)
    OP_DELAY, 0,           // delay(1)
    OP_JMP, 0, 0,          // бесконечный цикл
    OP_HALT
};

void setup() {
    Serial.begin(115200);
    pinMode(13, OUTPUT);
    
    VirtualMachine vm;
    vm.loadProgram(blinkProgram, sizeof(blinkProgram));
    vm.execute();
}

void loop() {}

Компилятор для нашего языка (на Python):

# compiler.py - конвертирует наш язык в байт-код
import sys

opcodes = {
    'LOAD': 0x01, 'STORE': 0x02, 'ADD': 0x03,
    'SUB': 0x04, 'MUL': 0x05, 'DIV': 0x06,
    'CMP': 0x07, 'JMP': 0x08, 'JZ': 0x09,
    'CALL': 0x0A, 'RET': 0x0B, 'PRINT': 0x0C,
    'READ': 0x0D, 'WRITE': 0x0E, 'DELAY': 0x0F,
    'HALT': 0x10
}

def compile(source):
    bytecode = []
    lines = source.strip().split('\n')
    
    for line in lines:
        parts = line.strip().split()
        if not parts:
            continue
        
        op = parts[0]
        bytecode.append(opcodes[op])
        
        for arg in parts[1:]:
            bytecode.append(int(arg))
    
    return bytes(bytecode)

# Пример программы на нашем языке
source_code = """
LOAD 0 1
LOAD 1 13
WRITE 1 0
DELAY 0
LOAD 0 0
WRITE 1 0
DELAY 0
JMP 0
HALT
"""

bytecode = compile(source_code)

# Сохраняем в C-массив
print("const uint8_t program[] = {")
for i, b in enumerate(bytecode):
    if i % 8 == 0:
        print("    ", end="")
    print(f"0x{b:02X}", end="")
    if i < len(bytecode) - 1:
        print(", ", end="")
    if i % 8 == 7:
        print()
print("};")

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *