Я работаю с запатентованным MCU, который имеет встроенную металлическую библиотеку (маска ПЗУ). Я использую компилятор clang, который использует GCC-подобный встроенный ASM. Проблема, с которой я сталкиваюсь, — это вызов библиотеки, поскольку у библиотеки нет согласованного соглашения о вызовах. В то время как я нашел решение, я обнаружил, что в некоторых случаях компилятор выполняет оптимизацию, которая регистрируется клоббером непосредственно перед вызовом, я думаю, что в моих действиях что-то не так. Вот код, который я использую:
int EchoByte()
{
register int asmHex __asm__ ("R1") = Hex;
asm volatile("//Assert Input to R1 for MASKROM_EchoByte":
:"r"(asmHex)
:"%R1");
((volatile void (*)(void))(MASKROM_EchoByte))(); //MASKROM_EchoByte is a 16-bit integer with the memory location of the function
}
Теперь возникает очевидная проблема: в то время как переменная «asmHex» утверждается для регистрации R1, реальный вызов не использует ее, и поэтому компилятор «не знает», что R1 зарезервирован во время вызова. Я использовал следующий код для устранения этого случая:
int EchoByte()
{
register int asmHex __asm__ ("R1") = Hex;
asm volatile("//Assert Input to R1 for MASKROM_EchoByte":
:"r"(asmHex)
:"%R1");
((volatile void (*)(void))(MASKROM_EchoByte))();
asm volatile("//Assert Input to R1 for MASKROM_EchoByte":
:"r"(asmHex)
:"%R1");
}
Это кажется мне очень уродливым, и как будто должен быть лучший способ. Также я обеспокоен тем, что компилятор может делать какую-то глупость между ними, поскольку сам вызов не имеет никаких признаков того, что ему нужна переменная asmHex. К сожалению, ((volatile void (*) (int)) (MASKROM_EchoByte)) (asmHex) не работает, так как будет следовать соглашению C, которое помещает аргументы в R2 + (R1 зарезервирован для царапин)
Обратите внимание, что изменить библиотеку Mask ROM, к сожалению, невозможно, и слишком много часто используемых подпрограмм, чтобы воссоздать их все в C / C ++.
Ура и спасибо.
РЕДАКТИРОВАТЬ: я должен отметить, что, хотя я мог бы вызвать функцию в блоке ASM, компилятор имеет оптимизацию для функций, которые не требуют вызова, и, вызывая сборку, похоже, что вызова нет. Я мог бы пойти по этому пути, если есть какой-то способ указать, что встроенный ASM содержит вызов функции, но в противном случае адрес возврата, вероятно, будет засорен. Я не смог найти способ сделать это в любом случае.
По комментариям выше:
Самый обычный ответ заключается в том, что вы должны реализовать функцию-заглушку в сборке (в .s
файл), который просто выполняет дурацкий вызов для вас. В ARM это будет выглядеть примерно так
// void EchoByte(int hex);
_EchoByte:
push {lr}
mov r1, r0 // move our first parameter into r1
bl _MASKROM_EchoByte
pop pc
Внедрите одну из этих заглушек в каждую подпрограмму mask-ROM, и все готово.
Что это такое? У вас есть 500 подпрограмм с маской и не хотите вырезать и вставить столько кода? Затем добавьте уровень косвенности:
// typedef void MASKROM_Routine(int r1, ...);
// void GeneralPurposeStub(MASKROM_Routine *f, int arg, ...);
_GeneralPurposeStub:
bx r0
Вызовите эту заглушку, используя синтаксис GeneralPurposeStub(&MASKROM_EchoByte, hex)
, Это будет работать для любой точки входа в маску-ROM, которая ожидает параметр в r1
, любой действительно для дурацких точек входа все равно потребуются собственные заглушки с ручным кодом.
Но если ты действительно, действительно, действительно необходимо сделать это с помощью встроенной сборки в функции C, а затем (как указал @JasonD) все, что вам нужно сделать, это добавить регистр ссылок lr
в глухой список.
void EchoByte(int hex)
{
register int r1 asm("r1") = hex;
asm volatile(
"bl _MASKROM_EchoByte":
: "r"(r1)
: "r1", "lr" // Compare the codegen with and without this "lr"!
);
}
Других решений пока нет …