Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: присоединение программы на ассемблере(asm) к программе на С
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
KosTTTT
Как присоединить большую программу, написанную в AVR Studio на ассемблере к программе на С? В интернете вроде бы прочел, что надо присоединить объектный файл программы на ассемблере на этапе линковки, но как это сделать?
Alex11
Две программы соединить нельзя. Кто-то из них должен быть программой, а кто-то подпрограммой, из нее вызываемой. Обычно на С пишется головная программа, а на ассемблере подпрограмма. В этом случае подпрограмма должна соблюдать некоторые ограничения, накладываемые С-компилятором. Далее, в Вашем случае будут, скорее всего, еще дополнительные сложности, т.к. AVR-студия не имеет своего С-компилятора. Вам нужно найти С-компилятор с линкером, который поймет формат объектного файла, сформированного AVR-студией. Либо транслировать программу, написанную под студию другим ассемблером. Здесь возможны расхождения в синтаксисе и куча правок руками. А в принципе, достаночно линкеру указать два модуля для линковки, и они соединятся.
KosTTTT
Объектный файл, сформированный AVR Stuio имеет формат .obj, а компилятор Си, GCC - .o. Я так понял, я не смогу их соединить на этапе линковки?
Кстати не могли бы вы объяснить, как мне в какой-нибудь среде (WinAVR, Eclipse, CVAVR) присоединять готовые объектные файлы, чтобы не компилировать по нескольку раз файлы .с?
=GM=
Есть несколько вариантов объединения.

1) Самый простой - можно объединить на уровне хекс-файлов. Компилируете ассемблерную подпрограмму (или программу в общем случае) с определенного адреса, а в сишной программе указываете адрес вызова (или перехода) перехода на данный адрес. Прошивки можно объединить на уровне хекс-текста, или прошить независимо, не стирая кусок, прошитый первым.

2) Оформляете ассемблерную подпрограмму в стиле, понятном компилятору cи, скажем, winavr. Ну там есть мощные ограничения, и вообще ублюдочно сделано на уровне компилятора. Вот яркий пример "ублюдочности"
Код
void static inline Signal_OUT(const uint8_t *signal, uint8_t ad2, uint8_t ad1, uint8_t ad0)
{
asm volatile(
   "eor r18, r18 ;r18<-0"                 "\n\t"
   "eor r19, r19 ;r19<-0"                 "\n\t"
"1:"                                     "\n\t"
  "add r18, %0;1 cycle"                "\n\t"
  "adc r19, %1;1 cycle"                "\n\t"    
  "adc %A3, %2;1 cycle"                "\n\t"
  "lpm     ;3 cycles"              "\n\t"
  "out %4, __tmp_reg__;1 cycle"    "\n\t"
  "sbis %5, 2;1 cycle if no skip" "\n\t"
  "rjmp 1b    ;2 cycles. Total 10 cycles"    "\n\t"
  :
  :"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(SPCR))
  :"r18", "r19"
);
}


3) Оформляете ассемблерную подпрограмму в отдельном файле с расширением .S Чуть попозже подготовлю пример оформления.
demiurg_spb
Цитата(=GM= @ Aug 28 2014, 11:24) *
Позволю себе прокомментировать..

В первый вариант я вообще не верю, т.к. программа на асме вряд ли написана структурно и она 100% будет портить СИ-контекст, так что можете об этом даже и не мечтать...

Второй вариант самый трудозатратный и лично я не вижу в нём хоть какого-нибудь смысла касательно перепахивания всей программы в таком стиле.
Ну а что касается "ублюдочности" формата асм вставок, так это плата за возможность отдать на откуп распределение регистров ассемблеру-компилятору.
И ни один другой инструмент не может в этом плане сравниться с gcc/gas.

ИМХО вариант 3 самый компромиссный, но придётся сделать нехилый рефакторинг асм кода (замечу, что может оказаться легче сразу на си переписывать вычлененные функ
ции).
=GM=
Цитата(demiurg_spb @ Aug 28 2014, 07:34) *
Позволю себе прокомментировать..
В первый вариант я вообще не верю, т.к. программа на асме вряд ли написана структурно и она 100% будет портить СИ-контекст, так что можете об этом даже и не мечтать...

Да ладно :-) С какой стати она будет портить си-контекст, который находится в пзу? Конечно, многое зависит от функциональности данной подпрограммы, и как она связана со всем остальным. Возьмём какой-нибудь пример, скажем, надо сделать два мака над 1000000 выборок и результат положить в две 8-байтные ячейки. Ассемблерная программа берет данные с АЦП, перемножает с синусом и косинусом из таблицы, кладёт результат в определенное место в озу и завершает работу. Сишная программа знает откуда взять результат, там место зарезервировано, и берет его. Вот и всё.
demiurg_spb
Цитата(=GM= @ Aug 28 2014, 12:22) *
Не так всё радужно.
У си контекста есть стек и иногда даже куча, есть область данных (инициализированных и нет) есть соглашения по использованию регистров,
а программа на асме об этом ничего не знает и не ведает, она обычно использует не только ПЗУ, но и ОЗУ и регистры как ей взбредёт в голову...
KosTTTT
Цитата(=GM= @ Aug 28 2014, 11:24) *
3) Оформляете ассемблерную подпрограмму в отдельном файле с расширением .S Чуть попозже подготовлю пример оформления.


Мне следует просто вставить код программы в созданный файл .S? Не будет ли при этом отличаться синтаксис от компилятора в AVR Studio? И как быть с файлами .inc, которые я использовал?
Нужно ли при этом оформлять заголовочный файл с прототипом функции, которую я вызову из главной программы на Си. И как мне оформить этот прототип? Не void function (void);?
=GM=
Ерунда на постном масле, причём здесь стек? Ну вызвали вы подпрограмму, ну взяли вы адрес ячеек, который сгенерила сишная программа, ну вызвали вы из си ассемблерную программу, ну так сохраните регистры, которые она использует. Тем более, что вызов подпрограммы можно оформить отдельной строкой (отдельным оператором), а сишная программа вроде бы не "пересекает" регистры разных операторов (без учёта оптимизации, конечно).

Ниже приведен обещанный пример объединения по третьему варианту. Вырвано из рабочего проекта, кое-что удалено, кое-что подправлено.
CODE
// File: example.c
// Title: Example
// Version: 1.4
// Last updated: 09.03.13
// Author: GM
//Target: ATmega324PA (16MHz operating frequency)
//
// Support E-mail: xxxxx@xxxx.ru
//
// DESCRIPTION
// The routine measures
#define F_CPU 16000000UL //16MHz
#define PI 3.14159265359L
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include <avr/pgmspace.h>

extern void mactest(void);
extern void asmroutine(void);

long long volatile re0=0;
long long volatile uval;
long volts[20] __attribute__ ((section (".block80")));
int ptra,ptrb;
unsigned char tab10k/ / __attribute__ ((section (".gentable")));
unsigned char tab10k/380/ =
{
128,137,147,157,167,176,185,194,
202,210,217,224,230,236,241,245,

248,251,253,254,255,254,253,251,
248,245,241,236,230,224,217,210
};

int main (void)
{
double num,R0;
int i,j;

mactest();

while(1) //loop forever
{
asmroutine();

re1=0;

im1=im1+(((long long)volts[j++])*((long long)sin10[i]));

}
}


;File: asmroutine.ASM
;Title: asm subroutine example
;Version: 1.1
;Last updated: 01.06.11
;Author: GM
;Target: ATmega324PA (16MHz operating frequency)
;
;Support E-mail: xxxxxx@xxxx.ru
;
;DESCRIPTION
; -----------------------

.nolist
#include <avr/io.h> // Include device specific definitions
.list
;#define tmp1 r16;
;#define rs 0
;#define rw 1
;#define e 2
;#define rst 6;
;#define cs 5;
;#define sclk 4;
;#define sda 3;

#define c0 r8
#define c1 r9
#define c2 r10
#define c3 r11
#define d0 r12
#define d1 r13
#define d2 r14
#define d3 r15
#define zl r30
#define zh r31

.global tab25k
.global re0
.global im0
.global asmroutine
.func asmroutine
asmroutine:
ldi ZL,lo8(tab25k)
ldi ZH,hi8(tab25k)

clr c0
clr c1

clr d1

asm1: lpm r0,Z+
lds c0,re0+0
lds c1,re0+1

add c0,d0

adc c1,d1
adc c2,d2

sts re0+1,c1
sts re0+2,c2
sts re0+3,c3

; rjmp asm1
ret

.endfunc
demiurg_spb
Цитата(KosTTTT @ Aug 28 2014, 13:33) *

Думаю вам стоит всё это прочитать:
http://microsin.net/programming/AVR/mixing...d-asm-code.html
http://www.nongnu.org/avr-libc/user-manual/FAQ.html
http://www.nongnu.org/avr-libc/user-manual/inline_asm.html


Цитата(=GM= @ Aug 28 2014, 13:38) *
ну вызвали вы из си ассемблерную программу
вся программа на АСМ - это не только ассемблерные подпрограммы. У неё есть и глобальные переменные. А стек тут при том что его можно по-разному организовать, можно как в avr-gcc (аппаратный), а можно как в IAR и ICC-AVR с использованием Y-регистра. Как его писатели асм-кода реализовали тоже не известно.
PS: В большинстве своём мне приходилось видеть asm-говнокод - простыню кода на ASM без какого бы то ни было структурирования, и что вы собираетесь вызывать из си?
Когда управление будет возвращено? Что будет с СИ контекстом?

Я понимаю, что вы говорите об идеальной ASM программе, написанной в стиле языка более высокого уровня, но это ваша иллюзия быстро развеется в реальной жизни.
Вы возможно отличный АСМ-программист, съевший не одну собаку, и на текущий момент перешедший/переходящий на Си.
И с высоты прожитых лет знаете как надо писать и как нет. Но таких людей мало. В основном - студенты, которые сразу рвутся в бой и катают простыню.
Я прошу вас, вернитесь к реалиям.

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

Далее, я не призываю писать весь код на ассемблере, в настоящее время генерируемый си-код, как правило, всего на 10-50% объемнее ассемблерного кода. Я призываю крепко подумать, прежде чем использовать асм п\п. Лично я не представляю себе как на си перемножить вышеупомянутую 64-битную переменную re0 на другую 64-битную переменную с получением 128-битного результата. Чем биться с сишными извращениями, мне проще написать подпрограмму на ассемблере. Вот это и есть реалистический подход.

Может быть пример и не показателен, это просто пример автору темы, как можно решить вставшую перед ним проблему.
demiurg_spb
Цитата(=GM= @ Aug 28 2014, 15:18) *
Всё давно придумано до нас.
Золотые слова. И демонстрируемые вами способы мне хорошо знакомы.
Цитата
Может быть пример и не показателен, это просто пример автору темы, как можно решить вставшую перед ним проблему.
Ну мы то друг-друга поняли.
Заключительная ремарка. Ваш совет хорош для создания программ с нуля (ваш случай) или для сращивания хорошо написанных на асме подпрограмм с си-программой.
В остальных случаях он уже не применим без больших усилий по переработке асм-простыни, о чём я не устаю повторять.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.