Jump to content

    
Sign in to follow this  
aivs

Код на Си в два раза больше чем на ASM. Attiny2313

Recommended Posts

Написал тестовую программу на Ассемблере и Си для Attiny2313.

Программа элементарна, при нажатии на кнопку загорается светодиод, при отпускании гасится.

Вот код на ассемблере:

;##########################################
;## Нажатие на кнопку включает светодиод ##
;##########################################

.include "/usr/share/avra/includes/tn2313def.inc"    ; Подключаем файл описаний
.list                ; Включаем листинг

.def    temp = R16        ; Определение главного рабочего регистра

    .cseg            ; Выбор сегмента программного кода
    .org    0        ; Установка текущего адреса на 0
        
    ldi    temp,    RAMEND    ; Записываем адрес вершины стека в регистр temp
    out    SPL,    temp    ; Записываем адрес вершины стека из регистра temp в регистр стека

    ldi    temp,    0    ; Записываем 0 в регистр temp
    out    DDRD,    temp    ; Записываем 0 из регистра temp в DDRD (порт PD на ввод - кнопка)
    ldi    temp,    0b11111111; Записываем 11111111 в регистр temp
    out    DDRB,    temp    ; Записываем 11111111 из регистра temp в DDRB (порт PB на вывод - LED)

    out    PORTB,     temp    ; Записываем 11111111 из temp в PORTB (тушим светодиод)
    out    PORTD,    temp    ; Записываем 11111111 из temp в PORTD (включаем внутр. резистры)

;--------------- Основной цикл ---------------------;

main:    in    temp,    PIND    ; Читаем содержимое порта PD
            out    PORTB,     temp    ; Записываем содержимое temp в порт PB
            rjmp    main        ; Переход к началу программы

Затем я дизассемблирил HEX файл, получился точно такой же код:

00000000 <.sec1>:
   0:   0f ed           ldi     r16, 0xDF      ; 223
   2:   0d bf           out     0x3d, r16      ; 61
   4:   00 e0           ldi     r16, 0x00      ; 0
   6:   01 bb           out     0x11, r16      ; 17
   8:   0f ef           ldi     r16, 0xFF      ; 255
   a:   07 bb           out     0x17, r16      ; 23
   c:   08 bb           out     0x18, r16      ; 24
   e:   02 bb           out     0x12, r16      ; 18
  10:   00 b3           in      r16, 0x10      ; 16
  12:   08 bb           out     0x18, r16      ; 24
  14:   fd cf           rjmp    .-6            ;  0x10

 

Вот код на Си:

/****************************************
* Prog1.c
* Нажатие на кнопку включает светодиод
****************************************/

#include <avr/io.h>
#include <avr/iotn2313.h>

void main(void) {
  
  DDRB    = 0b11111111; // Во всех разрядах регистра DDRB единицы --> все разряды PB работают на вывод (LED)
  PORTB = 0b11111111; // Во всех разрядах PB единицы и все разряды регистра DDRB работаю на вывод --> тушим Led
  
  DDRD    = 0b00000000; // Во всех разрядах регистра DDRD нули --> все разряды PD работают на ввод (кнопка)
  PORTD    = 0b11111111; // Во всех разрядах PD единицы и все разряды регистра DDRD работаю на ввод --> подключаем внутренние резистры
  
  // Вечный цикл
  while (1) {
    PORTB = PIND;
  }
}

 

После дизассемблера вид такой:

00000000 <.sec1>:
   0:   12 c0           rjmp    .+36           ;  0x26
   2:   17 c0           rjmp    .+46           ;  0x32
   4:   16 c0           rjmp    .+44           ;  0x32
   6:   15 c0           rjmp    .+42           ;  0x32
   8:   14 c0           rjmp    .+40           ;  0x32
   a:   13 c0           rjmp    .+38           ;  0x32
   c:   12 c0           rjmp    .+36           ;  0x32
   e:   11 c0           rjmp    .+34           ;  0x32
  10:   10 c0           rjmp    .+32           ;  0x32
  12:   0f c0           rjmp    .+30           ;  0x32
  14:   0e c0           rjmp    .+28           ;  0x32
  16:   0d c0           rjmp    .+26           ;  0x32
  18:   0c c0           rjmp    .+24           ;  0x32
  1a:   0b c0           rjmp    .+22           ;  0x32
  1c:   0a c0           rjmp    .+20           ;  0x32
  1e:   09 c0           rjmp    .+18           ;  0x32
  20:   08 c0           rjmp    .+16           ;  0x32
  22:   07 c0           rjmp    .+14           ;  0x32
  24:   06 c0           rjmp    .+12           ;  0x32
  26:   11 24           eor     r1, r1
  28:   1f be           out     0x3f, r1       ; 63
  2a:   cf ed           ldi     r28, 0xDF      ; 223
  2c:   cd bf           out     0x3d, r28      ; 61
  2e:   02 d0           rcall   .+4            ;  0x34
  30:   09 c0           rjmp    .+18           ;  0x44
  32:   e6 cf           rjmp    .-52           ;  0x0
  34:   8f ef           ldi     r24, 0xFF      ; 255
  36:   87 bb           out     0x17, r24      ; 23
  38:   88 bb           out     0x18, r24      ; 24
  3a:   11 ba           out     0x11, r1       ; 17
  3c:   82 bb           out     0x12, r24      ; 18
  3e:   80 b3           in      r24, 0x10      ; 16
  40:   88 bb           out     0x18, r24      ; 24
  42:   fd cf           rjmp    .-6            ;  0x3e
  44:   f8 94           cli
  46:   ff cf           rjmp    .-2            ;  0x46

 

Меня интересует, что это за множество переходов в начале, для чего их делает компилятор?

Компилирую так:

avr-gcc -O2 -Os -mmcu=attiny2313 -o Prog1.elf Prog1..c

avr-objcopy -R .eeprom -O ihex Prog1.elf Prog1.hex

Share this post


Link to post
Share on other sites
Меня интересует, что это за множество переходов в начале, для чего их делает компилятор?

Похоже на таблицу векторов прерываний.

З.Ы. Компилировать лучше с использованием специального скрипта - Makefile. Редко компилятору нужно задавать менее двух - трех ключей. Следовательно их много, и проще воспользоваться предназначенным для этого файлом, где все это удобно и четко прописано. Оптимизации в подавляющем большинстве достаточно -Os. Возможно она удалит неиспользуемую таблицу векторов.

Share this post


Link to post
Share on other sites
Похоже на таблицу векторов прерываний. ... Возможно она удалит неиспользуемую таблицу векторов.

Это таблица векторов прерываний и есть. Оптимизация не удалит строки в неиспользуемой части таблицы, поскольку это - своеобразная защита "от дурака" на случай если программист случайно разрешит какое-либо прерывание, а соответствующий обработчик написать забудет.

Конечно, при столь маленькой программе "лишние" несколько десятков байт в программе "смотрятся" несколько удивительно, но:

1. При "нормальной" программе эти "лишние" байты - капля в море.

2. Программа на языке высокого уровня при любой оптимизации будет несколько "хуже" программы на ассемблере. Но время на разработку, отладку и сопровождение ПО уменьшается в разы. Отдельные части программы можно написать и на ассемблере (если это необходимо).

Share this post


Link to post
Share on other sites

То что код на Си всегда будет больше чем на ассемблере это понятно.

Конкретно эту таблицу векторов прерываний как удалить из этой прошраммы?

Или если дайте почитать про так написать Makefile с нужными опциМи компиляции.

Свои примеры приветствуются!

Edited by aivs

Share this post


Link to post
Share on other sites
Конкретно эту таблицу векторов прерываний как удалить из этой программы?

1)Взять тут файл стартапа.

2)Отредактировать его, выкинув лишнее.

3)Ассемблировать.

4)Слинковать с основным модулем добавив линкеру ключ -nostartfiles.

5)Немного дописать основную программу

6)Убить кучу времени пытаясь понять, почему она перестала работать

7)Понять, что в п.2 удалили лишнее :)

Share this post


Link to post
Share on other sites
+1

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

Есть даже нечто рабочее в виде патча. Но до коммита в транк ещё видимо очень далеко.

Share this post


Link to post
Share on other sites
Написал тестовую программу на Ассемблере и Си для Attiny2313.

Программа элементарна, при нажатии на кнопку загорается светодиод, при отпускании гасится.

Вот код на ассемблере:

.....

Затем я дизассемблирил HEX файл, получился точно такой же код:

....

 

Для начала, нужно вернуться к истокам и разобраться, почему "Дизасемблированный HEX файл" из "программы на Ассемблере" дает "точно такой же код".

После этого нужно ответить на вопрос, почему дизассемблированный HEX файл, полученный в результате компиляции из C, содержит другие команды чем исходный Си текст.

И только после этого можно приступать к решению проблемы, которую Вы подняли.

 

Это базовые знания. Без этого понимания сложно что-то делать. Повторять чужое-можно, делать свое хорошего качества и понимать почему результат такой- не получится.

 

Кстати, еще полезно задуматься о том, а зачем Вам эта оптимизация по размеру? как самоцель? Оптимизация должна быть аргументированная и производиться с какой-то целью (например, для достижения необходимой скорости реакции, времени выполнения, размера программы). Просто так оптимизировать чтобы было оптимально- это бесконечный и никому не нужный процесс.

 

2. Программа на языке высокого уровня при любой оптимизации будет несколько "хуже" программы на ассемблере. Но время на разработку, отладку и сопровождение ПО уменьшается в разы. Отдельные части программы можно написать и на ассемблере (если это необходимо).

Например, у компиляторов есть такая интересная оптимизация как "Procedural Abstraction". Так оно иногда такие хитрые финты выкидывает по многократному использованию кода, что в здравом уме на ассме долго думать придется чтобы такой компактности достичь :)

Про "в разы меньше времени на Си чем на ассме"- это Вы не правы. Не в разы, а в тысячи и миллионы раз легче, особенно если речь идет о сопровождении (Само собой, я не говорю о лампочке).

Share this post


Link to post
Share on other sites
1)Взять тут файл стартапа.

2)Отредактировать его, выкинув лишнее.

3)Ассемблировать.

4)Слинковать с основным модулем добавив линкеру ключ -nostartfiles.

5)Немного дописать основную программу

6)Убить кучу времени пытаясь понять, почему она перестала работать

7)Понять, что в п.2 удалили лишнее :)

:biggrin: полностью поддерживаю, особенно пункт 7 вас должен натокнуть на мысль о том что не стоит оптимизировать что-либо пока вы не достигли размера flash. Вам для чего выйгрыш в нескольких десятках байт?

Share this post


Link to post
Share on other sites
а в тысячи и миллионы раз легче, особенно если речь идет о сопровождении (Само собой, я не говорю о лампочке).
То есть Вы утверждаете, что потратите час на написание или сопровожддение некого кода на С, а тот же код на асме (применительно к 8 бит Атмелу, а не GUI или СУБД больших машинах) будет отработан за 10е6 часов то есть 114 лет? Что-то неверится. Применительно к "мелким" 8 бит МК может и не нескольких раз не набраться.

Share this post


Link to post
Share on other sites
То есть Вы утверждаете, что потратите час на написание или сопровожддение некого кода на С, а тот же код на асме (применительно к 8 бит Атмелу, а не GUI или СУБД больших машинах) будет отработан за 10е6 часов то есть 114 лет? Что-то неверится. Применительно к "мелким" 8 бит МК может и не нескольких раз не набраться.

Скорее так: нужно по-быстрому поменять несколько мест в нескольких программах (объемом 10К строк). Может алгоритм поменять, может тип данных, структурку и т.п. На C такие действия тривиальны, на ASM врагу не пожелаешь))

Да, и к Ctrl-C, Ctrl-V вариант C дружественнее...

Share this post


Link to post
Share on other sites
пункт 7 вас должен натокнуть на мысль о том что не стоит оптимизировать что-либо пока вы не достигли размера flash.

Не стоит отпимизировать...

Гораздо лучше вести проект с выключенной оптимизацией и когда он достигнет размера flash (килобайт 100-200) вдруг возникнет необходимость ещё в десятке-другом кБ, а его и нет...

И вы окажетесь с проектом на несколько десятков тысяч строк кода, который неизвестно как себя поведёт при включённой оптимизации.

Share this post


Link to post
Share on other sites

оптимизация на С, С++ дает хорошую экономию места, но некоторые места (например дрыганье выводами) приходиться "защищать" от оптимизации, иначе компилятор может выкинуть часть кода.

 

с ассемблерном много возни, особенно на переходы с близких на дальние.

были проекты до 8К асм с каруселькой на 7 процессов

 

Итого: если плохо знаешь свой инструментарий, то будет плохо, иначе наоборот

Share this post


Link to post
Share on other sites
Например, у компиляторов есть такая интересная оптимизация как "Procedural Abstraction". Так оно иногда такие хитрые финты выкидывает по многократному использованию кода, что в здравом уме на ассме долго думать придется чтобы такой компактности достичь :)

а можно про эту штуку поподробнее ?

есть ли она на avr-gcc и если да, то где она включается ?

Спасибо)

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.

Sign in to follow this