Доступ к статической переменной C++ из встроенного asm gcc

По какой-то причине я хочу смешать немного ассемблера с C++ (не C).
Чтобы код можно было скомпилировать с помощью стандартной среды разработки Arduino, я не хочу использовать исходный код прямой сборки.
Я также не хочу использовать файл-оболочку C.

Моя проблема заключается в следующем:

У меня есть класс с одним экземпляром (реализующим какой-то драйвер), который выглядит так:

class cDriver {
public:
    char ISR_param;
    // ...
};

cDriver Driver;

Если я хочу получить доступ к ISR_param этого единственного экземпляра из ISR, я могу сделать что-то вроде:

ISR (TIMER2_COMPA_Vect) {
    do_something_with (Driver.ISR_param);
}

Это работает как шарм: адрес Driver.ISR_param становится константой, компилятор просто генерирует инструкцию lds r24, &Driver.ISR_param для загрузки правильный байт в качестве параметра для do_something_with.

Теперь, когда я пытаюсь воспроизвести тот же механизм с помощью встроенного ассемблера, все становится намного сложнее.

Адрес Driver.ISR_param известен компилятору C++ только как искаженная метка, и я не могу найти способ передать его встроенному ассемблеру для создания эквивалентного lds r24, <&Driver.ISR_param>.

пытается переименовать переменную

Переименование переменных для сборки примерно так:

char global_var asm("my_var");

и попытка использовать его вот так:

asm ("lds r24, my_var\n");

будет компилироваться, но выдаст ошибку компоновщика (my_var не определено).

Глядя на таблицу символов объектов, единственная метка для global_var — это искаженное имя, подобное C++.
Как бы я ни старался использовать этот трюк с переименованием, мне не удалось заставить компилятор сгенерировать метку my_var.

пытается использовать ограничения asm

Ограничение типа

asm (lds r24, %0\n" : : "I"(&global_var));

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

ужасно неудобный обходной путь

Единственный найденный мной способ заставить компилятор генерировать эту ужасную инструкцию lds r24,... — это выполнить реальный вызов функции.

Итак, вы можете сделать что-то вроде:

__attribute__((always_inline)) give_me_my_friggin_variable(char data)
{
    asm volatile ("cmp  %0,%0\n"::"r"(data));
}   

ISR (TIMER2_COMPA_Vect, ISR_NAKED) {

    <some prologue to save SREG and all needed registers>

    //...

    give_me_my_friggin_variable(Driver.ISR_param);

    // продолжить ассемблерный код с правильно инициализированным r24
}

Это создаст:

    lds r24, <the proper address> // добавлено компилятором...
    cmp r24,r24                   // ...из-за ограничения регистра здесь

Ну, это работает, но за счет создания совершенно бесполезной инструкции.

Итак, мой вопрос...

Есть ли более простой способ сообщить ассемблеру адреса статических переменных C++?

, 👍7


1 ответ


Лучший ответ:

4

Попробуйте это:

asm volatile("lds r24, %0" : : "i" (&Driver.ISR_param));

Ограничение "i" означает, что вы указываете целочисленную константу. Может быть любое целое число, в отличие от ограничения "I" в верхнем регистре, которое ограничено 6-битные положительные целые числа.

Дополнение: Ограничения, поддерживаемые gcc, перечислены в документации gcc, раздел Ограничения для операндов asm:

  • Ограничение "i" указано в подразделе "Простые ограничения". и описывается как:

Допускается непосредственный целочисленный операнд (один с постоянным значением). Сюда входят символические константы, значения которых будут известны только в время сборки или позже.

  • Ограничение "I" находится в подразделе "Ограничения компьютера" и, для архитектуры AVR определяется как

Константа больше -1, меньше 64.

,

Черт... И спасибо :). Этот тип «i» не был указан в онлайн-руководствах, которые я нашел. Для полноты, не могли бы вы добавить ссылку на актуальную документацию? Кстати. Интересно, почему так трудно найти пример такого очевидного использования встроенного ассемблера., @kuroi neko

@kuroineko: ограничение "i" действительно отсутствует в [Поваренной книге встроенного ассемблера] avr-libc (http://www.nongnu.org/avr-libc/user-manual/inline_asm.html). Я добавил ссылку на документацию gcc, в которой он указан., @Edgar Bonet

Большое спасибо. Думаю, такая кулинария довольно конфиденциальна :)., @kuroi neko

Ограничение "i" **не означает "целое число"**. "i" задает непосредственный операнд, который представляет собой объединение "n" (целочисленный операнд, известный во время компиляции) и символического операнда "s", известный во время компоновки. Например, адреса переменных в статическом хранилище известны во время компоновки, поэтому для них будет работать "i"`., @emacs drives me nuts