Jump to content

    

Вставка файла ассемблера .s в файл main.c

Recommended Posts

AzardCry

Здравствуйте!

 

В своём проекте создал 5 файлов - main (.c, .h), PortInit (.c, .h), RegSum (.s). Собственно, весь вопросы в том, что:

1. А так вообще кто-то делает, или намного легче через __ASM написать всё в .c-файле?

2. Как правильно организовать включение текста файла RegSum.s (на языке ассемблера) в main.c, с учётом того, что это именно отдельный файл в дереве проекта, а не вставка в main.c

3. Судя по некоторым темам на форуме, есть трудности при организации функции, телом которой является .s-файл (напр, команды среды END, ENDP), разве среда не должна их выкинуть при компиляции, оставив только "железные" команды?

4. Также, немного не в теме вопроса, но:

        "Если хотя бы один из ISR, на которые ссылается таблица прерываний, определён в другом файле (а не в этом .asm), то указатель на него будет не константой. Он будет браться на этапе компоновки из таблиц экспорта .obj-файлов. А значит на этапе компиляции он неизвестен.

        "А зачем там пролог? __attribute__((naked)) скорее" З.Ы. имеется ввиду пролог функции.

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

 

Share this post


Link to post
Share on other sites

Arlleex
4 часа назад, AzardCry сказал:

1. А так вообще кто-то делает, или намного легче через __ASM написать всё в .c-файле?

А какая разница, кто как делает?:smile: У меня есть .c-файлы, где функции написаны на asm (вставками).
А вот startup-файл - весь на ассемблере, скорее лишь потому, что я беру его стандартный из коробки Keil, и допиливаю при необходимости.

Цитата

2. Как правильно организовать включение текста файла RegSum.s (на языке ассемблера) в main.c, с учётом того, что это именно отдельный файл в дереве проекта, а не вставка в main.c

Не надо его "вставлять". Надо из этого RegSum.s экспортировать имена используемых процедур (функций) и вызывать их где Вам нужно, в том же main.c.

Цитата

3. Судя по некоторым темам на форуме, есть трудности при организации функции, телом которой является .s-файл (напр, команды среды END, ENDP), разве среда не должна их выкинуть при компиляции, оставив только "железные" команды?

END, ENDP - это не ассемблерные команды. Это директивы. Никак не трансформируются в машинные команды.

Цитата

Если хотя бы один из ISR, на которые ссылается таблица прерываний, определён в другом файле (а не в этом .asm), то указатель на него будет не константой. Он будет браться на этапе компоновки из таблиц экспорта .obj-файлов. А значит на этапе компиляции он неизвестен.

        "А зачем там пролог? __attribute__((naked)) скорее" З.Ы. имеется ввиду пролог функции.

Вообще не понял. Еще раз и помедленнее.

Цитата

Признаюсь, стандарты языков и документацию на Keil до конца не читал...

Начать с гугления, как происходит трансляция исходных файлов программы в исполняемый (препроцессирование, компиляция, линковка).

Share this post


Link to post
Share on other sites

jcxz
4 часа назад, AzardCry сказал:

1. А так вообще кто-то делает, или намного легче через __ASM написать всё в .c-файле?

Раз возможность есть, значит кому-то это нужно?

Цитата

2. Как правильно организовать включение текста файла RegSum.s (на языке ассемблера) в main.c

Никак. Потому как ассемблер и си - это разные языки. И файлы *.s и *.c имеют разный формат.

.c-файлы компилирует си-компилятор, .s-файлы (или .asm) - транслятор ассемблера. Оба на выход выдают .obj-файлы. Которые поглощает компоновщик (являются для него входными). На выходе компоновщика - загрузочный образ прошивки (в формате: .hex, .bin или каком-то другом).

Цитата

3. Судя по некоторым темам на форуме, есть трудности при организации функции, телом которой является .s-файл

Галиматью какую-то пишете. .s-файл не может "являться телом функции".

Цитата

        "Если хотя бы один из ISR, на которые ссылается таблица прерываний, определён в другом файле (а не в этом .asm), то указатель на него будет не константой. Он будет браться на этапе компоновки из таблиц экспорта .obj-файлов. А значит на этапе компиляции он неизвестен.

И что?

Цитата

таблицы экспорта

Вам, как начинающему, это не нужно.

Изучайте примеры проектов. В многих средах программирования есть множество примеров.

Share this post


Link to post
Share on other sites

Darth Vader
5 часов назад, AzardCry сказал:

В своём проекте создал 5 файлов - main (.c, .h), PortInit (.c, .h), RegSum (.s). Собственно, весь вопросы в том, что:

Вы цель свою конечную опишите. Зачем вам нужен именно ассемблерный файл в проекте? Просто, чтобы был? Вы эту функцию на Си написать не можете? Вам ассемблерного стартап-файла мало?

Share this post


Link to post
Share on other sites

AzardCry
21.02.2022 в 19:01, Arlleex сказал:

А какая разница, кто как делает?:smile: У меня есть .c-файлы, где функции написаны на asm (вставками).
А вот startup-файл - весь на ассемблере, скорее лишь потому, что я беру его стандартный из коробки Keil, и допиливаю при необходимости.

Не надо его "вставлять". Надо из этого RegSum.s экспортировать имена используемых процедур (функций) и вызывать их где Вам нужно, в том же main.c.

END, ENDP - это не ассемблерные команды. Это директивы. Никак не трансформируются в машинные команды.

Вообще не понял. Еще раз и помедленнее.

Начать с гугления, как происходит трансляция исходных файлов программы в исполняемый (препроцессирование, компиляция, линковка).

 

21.02.2022 в 19:18, jcxz сказал:

Раз возможность есть, значит кому-то это нужно?

Никак. Потому как ассемблер и си - это разные языки. И файлы *.s и *.c имеют разный формат.

.c-файлы компилирует си-компилятор, .s-файлы (или .asm) - транслятор ассемблера. Оба на выход выдают .obj-файлы. Которые поглощает компоновщик (являются для него входными). На выходе компоновщика - загрузочный образ прошивки (в формате: .hex, .bin или каком-то другом).

Галиматью какую-то пишете. .s-файл не может "являться телом функции".

И что?

Вам, как начинающему, это не нужно.

Изучайте примеры проектов. В многих средах программирования есть множество примеров.

 

21.02.2022 в 19:51, Darth Vader сказал:

Вы цель свою конечную опишите. Зачем вам нужен именно ассемблерный файл в проекте? Просто, чтобы был? Вы эту функцию на Си написать не можете? Вам ассемблерного стартап-файла мало?

 

Доброго времени, Arlleex, jcxz, Darth Vader!

2. Arlleex, а можно короткий пример того, как процедура из файла RegSum.s будет перенесена в main.c?

    jcxz, Arlleex, судя по всему, не согласен с вами, либо имеет ввиду что-то другое (мне сложно сказать).

    Darth Vader, я исключительно в образовательных целях хочу создать три файла: два .c, один .s; один .c (main) основной; из второго .c (PortInit) я делаю своего рода библиотеку для выноса части кода; третий .s (RegSum) схож по назначению с PortInit.c, но на языке ассемблера. Функцию на Си написать могу, но тогда образовательная ценность моей задумки упадёт. Задача в том чтобы из PortInit и RegSum вставлять функции в main.c, но как сделать это для последнего я не знаю, потому что не работал с ассемблером до сих пор.

 

3. jcxz, может и галиматью, заранее прошу прощения за ошибки в терминологии, сам пока не до конца разобрался :)

Edited by AzardCry
Правки

Share this post


Link to post
Share on other sites

Arlleex
26 минут назад, AzardCry сказал:

jcxz, Arlleex, судя по всему, не согласен с вами, либо имеет ввиду что-то другое (мне сложно сказать)...

Почему? Не вижу противоречий:smile: А лучше конкретезируйте, в чем конкретно я, возможно, не согласен с jcxz.

Цитата

Arlleex, а можно короткий пример того, как процедура из файла RegSum.s будет перенесена в main.c?

.s

Скрытый текст
  PRESERVE8
  THUMB
  AREA |.text|, CODE, READONLY

SwitchContext
  PROC
    EXPORT SwitchContext
  
    push {lr}
    push {r1-r12}
    mrs   r1, XPSR
    push {r1}
  
    ...
  
    pop  {r1}
    msr  XPSR, r1
    pop  {r1-r12}
    pop  {pc}
  ENDP
  ALIGN
  END

.h

Скрытый текст
#ifndef EXAMPLE_H
#define EXAMPLE_H

void SwitchContext();

#endif

.c (например, где-то в main.c)

Скрытый текст
#include "example.h"

void func(void) {
  int a;
  int b;
  ...
  SwitchContext();
}

Share this post


Link to post
Share on other sites

AzardCry
24.02.2022 в 14:14, Arlleex сказал:

Почему? Не вижу противоречий:smile: А лучше конкретезируйте, в чем конкретно я, возможно, не согласен с jcxz.

.s

  Показать содержимое

  PRESERVE8
  THUMB
  AREA |.text|, CODE, READONLY

SwitchContext
  PROC
    EXPORT SwitchContext
  
    push {lr}
    push {r1-r12}
    mrs   r1, XPSR
    push {r1}
  
    ...
  
    pop  {r1}
    msr  XPSR, r1
    pop  {r1-r12}
    pop  {pc}
  ENDP
  ALIGN
  END

.h

  Показать содержимое

#ifndef EXAMPLE_H
#define EXAMPLE_H

void SwitchContext();

#endif

.c (например, где-то в main.c)

  Показать содержимое

#include "example.h"

void func(void) {
  int a;
  int b;
  ...
  SwitchContext();
}

Доброго времени, Arllex!

 

Благодарю за пример! Не подскажите, как сделать ту же функцию, но только принимающую значение из .c-файла и возвращающую в него же результат:

 

Насколько я успел понять до этого времени, аргументы функции переносятся в регистры с 0-го по N-ый соответственно по порядку из записи в "()", а вернуть их можно с помощью команды RET. На сайте ПО (сайт Keil) написано, что эта (RET) команда возвращает данные из стека (также в примерах фигурирует BX LR). Собственно, как именно передать условный аргумент "FirstNum" и вернуть из RegSum.s в main.c значение, присвоив его какой-либо переменной, чтобы потом использовать его далее, остаётся не ясным. Прошу пояснить или указать мне на материалы, которые проясняют данный вопрос.

 

Также интересно, почему для того, чтобы функция "видела себя" в различных файлах, в .s необходима "EXPORT", а в Си (.h-файле) никакой директивы для этого не предусмотрено.

 

"Никак. Потому как ассемблер и си - это разные языки. И файлы *.s и *.c имеют разный формат." - Касательно несогласия вас и jcxz - уже вижу, что разногласий нет. Скорее я неверно сформулировал вопрос, изначально, конечно, имелись ввиду вызовы функций из файлов различного формата, пример чего вы и предоставили.

Edited by AzardCry

Share this post


Link to post
Share on other sites

aaarrr
24 minutes ago, AzardCry said:

Насколько я успел понять до этого времени, аргументы функции переносятся в регистры с 0-го по N-ый соответственно по порядку из записи в "()", а вернуть их можно с помощью команды RET.

Гуглите документ под названием AAPCS. Значение возвращается в нулевом регистре, никаких команд не нужно.

 

25 minutes ago, AzardCry said:

Также интересно, почему для того, чтобы функция "видела себя" в различных файлах, в .s необходима "EXPORT", а в Си (.h-файле) никакой директивы для этого не предусмотрено.

В Си статические функции тоже "не видны", считайте, что в ассемблере они статические по умолчанию.

Share this post


Link to post
Share on other sites

jcxz
1 час назад, AzardCry сказал:

Благодарю за пример! Не подскажите, как сделать ту же функцию, но только принимающую значение из .c-файла и возвращающую в него же результат:

 

Насколько я успел понять до этого времени, аргументы функции переносятся в регистры с 0-го по N-ый соответственно по порядку из записи в "()", а вернуть их можно с помощью команды RET. На сайте ПО (сайт Keil) написано, что эта (RET) команда возвращает данные из стека (также в примерах фигурирует BX LR).

Да уж.... С такой кашей в голове рано что-то писать на asm.  :unknw:

"функцию, но только принимающую значение из .c-файла и возвращающую в него же результат" - no comments.  :crazy:

RET vs BX LR: в тех ЦП, в которых есть RET, нету BX LR. А в которых есть BX LR - нету RET. Определитесь всё таки - о каком ЦП ведёте речь? Иначе не понять - что и куда у вас "переносится". Ибо в разных CPU/компиляторах/соглашениях вызова - разные регистры и разный порядок передачи аргументов.

 

PS: Читайте параграф "Соглашения вызова" ("Calling conventions") в документации на ваш компилятор. Что-то типа:  1466738765_IARforARMcallingconventions.png.2cc72c2157a3737c6344f1dcc9b8bb7f.png

Share this post


Link to post
Share on other sites

AzardCry
11.03.2022 в 14:03, aaarrr сказал:

Гуглите документ под названием AAPCS. Значение возвращается в нулевом регистре, никаких команд не нужно.

 

В Си статические функции тоже "не видны", считайте, что в ассемблере они статические по умолчанию.

Доброго дня, Aaarrr! Благодарю за ответ! Не подскажете ли в завершение этого вопроса, как мне понять (В документе AAPCS сказано, что аргументы переносятся в регистры R0-R3, а возвращаемое значение - в R0...):

 

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

2. SimpleSum в итоге есть адрес ячейки в памяти программ (что видно при отладке) и выполнение программы переходит на него. Где заканчивается выполнение этой функции? В конце процедуры start (т.е. в ассемблере данный символ является просто меткой в процедуре, где компилятор обозначает конец её "тела")?

3. Keil пишет, что GLOBAL и EXPORT в ассемблере - синонимы. Есть ли разница в использовании. которая определяет применение каждой директивы в том или ином случае?

 

Мой код Си в заголовочном файле:

 

extern int SimpleSum(int Value);

 

Вызов функции в исполняемом файле:

 

SimpleSum(FirstNum); //помещаю значение переменной FirstNum для использования в вычислениях

 

Часть ассемблерного кода в области DATA:

 

GLOBAL SimpleSum

 

Часть ассемблерного кода в области CODE

 

start PROC

 

SimpleSum

    ....Здесь ассемблерный код (два цикла с метками Loop1(2) и командами между ним)...

    MOV R0, R3 ;Move value to R0 to return

ENDP

ALIGN

END

Share this post


Link to post
Share on other sites

aaarrr
2 hours ago, AzardCry said:

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

int x = SimpleSum(FirstNum);

 

2 hours ago, AzardCry said:

2. SimpleSum в итоге есть адрес ячейки в памяти программ (что видно при отладке) и выполнение программы переходит на него. Где заканчивается выполнение этой функции? В конце процедуры start (т.е. в ассемблере данный символ является просто меткой в процедуре, где компилятор обозначает конец её "тела")?

С точки зрения ассемблера конец функции - ENDP. С точки зрения процессора концом является команда возврата, которая в приведенном коде отсутствует (напр., mov pc, lr).

 

2 hours ago, AzardCry said:

3. Keil пишет, что GLOBAL и EXPORT в ассемблере - синонимы. Есть ли разница в использовании. которая определяет применение каждой директивы в том или ином случае?

Разницы нет.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.