Встроенный ассемблер c. Введение в язык ассемблера. Программирование на ассемблер

Что такое Ассемблер

Ассемблер - низкоуровневый язык программирования. Для каждого процессора существует свой ассемблер. Программируя на ассемблере вы непосредственно работаете с аппаратурой компьютера. Исходный текст на языке ассемблера состоит из команд (мнемоник), которые после компиляции преобразуются в коды команд процессора.

Разработка программ на ассемблере - очень тяжёлая штука. Взамен потраченному времени вы получаете эффективную программу. Программы на ассемблере пишут, когда важен каждый такт процессора. На ассемблере вы даёте конкретные команды процессору и никакого лишнего мусора. Этим и достигается высокая скорость выполнения вашей программы.

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

  1. Микропроцессора
  2. Памяти
  3. Устройств ввода/вывода.

Программная модель хорошо описана в литературе .

Синтаксис Ассемблера

Общий формат строки программы на ассемблере

<Метка>: <Оператор> <Операнды> ; <Комментарий>

Поле метки. Метка может состоять из символов и знаков подчёркивания. Метки используются в операциях условного и безусловного перехода.

Поле оператора. В этом поле содержится мнемоника команды. Например мнемоника mov

Поле операндов. Операнды могут присутствовать только если присутствует Оператор (поле оператора). Операндов может не быть, а может быть несколько. Операндами могут быть данные, над которыми необходимо выполнить какие-то действия (переслать, сложить и т.д.).

Поле комментария. Комментарий нужен для словесного сопровождения программы. Всё, что стоит за символом ; считается комментарием.

Первая программа на языке Ассемблера

В этой статье будет использоваться ассемблер для i80x86 процессора и использоваться следующее программное обеспечение:

  • TASM - Borland Turbo Assembler - компилятор
  • TLINK - Borland Turbo Linker - редактор связей (компоновщик)

Если быть конкретным, то Tasm 2.0.

По традиции наша первая программа будет выводить строку "Hello world!" на экран.

Файл sample.asm

Model small ; Модель памяти.stack 100h ; Установка размера стека.data ; Начало сегмента данных программы HelloMsg DB "Hello World!",13,10,"$" .code ; Начало сегмента кода mov ax,@DATA ; Пересылаем адрес сегмента данных в регистр AX mov ds,ax ; Установка регистра DS на сегмент данных mov ah,09h ; DOS функция вывода строки на экран mov dx,offset HelloMsg ; Задаём смещение к началу строки int 21h ; Выводим строку mov ax,4C00h ; DOS функция выхода из программы int 21h ; Выход из программы end

Как вы могли заметить, что программа разделена на сегменты: сегмент данных, сегмент кода и есть ещё стековый сегмент.

Рассмотрим всё по порядку.

Директива.model small задаёт модель памяти. Модель small - это 1 сегмент для кода, 1 сегмент для данных и стека т.е. данные и стек находятся в одном сегменте. Бывают и другие модели памяти, например: tiny, medium, compact. В зависимости от выбранной вами модели памяти сегменты вашей программы могут перекрываться или могут иметь отдельные сегменты в памяти.

Директива.stack 100h задаёт размер стека. Стек необходим для сохранения некоторой информации с последующим её восстановлением. В частности стек используется при прерываниях. В этом случае содержимое регистра флагов FLAGS, регистра CS и регистра IP сохраняются в стеке. Далее идёт выполнение прерывающей программы, а потом идёт восстановление значений этих регистров.

  • Регистр флагов FLAGS содержит признаки, которые формируются после выполнения команды процессором.
  • Регистр CS (Code Segment) содержит адрес сегмента кода.
  • Регистр IP (Instruction Pointer) - указатель команд. Он содержит адрес команды, которая должная выполниться следующей (Адрес относительно сегмента кода CS).

Более подробное описание выходит за рамки простой статьи.

Директива.data определяет начало сегмента данных вашей программы. В сегменте данных определяются "переменные" т.е. идёт резервирование памяти под необходимые данные. После.data идёт строка
HelloMsg DB "Hello World!",13,10,"$"

Здесь HelloMsg - это символьное имя, которое соответствует началу строки "Hello World!" (без кавычек). То есть это адрес первого символа нашей строки относительно сегмента данных. Директива DB (Define Byte) определяет область памяти доступную по-байтно. 13,10 - коды символов Новая строка и Возврат каретки, а символ $ необходим для корректной работы DOS функции 09h. Итак, наша строка будет занимать в памяти 15 байт.

Директива.code определяет начало сегмента кода (CS - Code Segment) программы. Далее идут строки программы содержащие мнемоники команд.

Расскажу о команде mov.

mov <приёмник>, <источник>

Команда mov - команда пересылки. Она пересылает содержимое источника в приёмник. Пересылки могут быть регистр-регистр, регистр-память, память-регистр, а вот пересылки память-память нет т.е. всё проходит через регистры процессора.

Чтобы работать с данными необходимо настроить регистр сегмента данных. Настройка состоит в том, что мы записываем адрес сегмента данных @DATA в регистр DS (Data Segment). Непосредственно записать адрес в этот регистр нельзя - такова архитектура, поэтому мы используем регистр AX. В AX мы записываем адрес сегмента кода

а потом пересылаем содержимое регистра AX в регистр DS.

После этого регистр DS будет содержать адрес начала сегмента данных. По адресу DS:0000h будет содержаться символ H. Я предполагаю, что вы знаете о сегментах и смещениях.

Адрес состоит из двух составляющих <Сегмент>:<Смещение>, где Сегмент это 2 байта и смещение - 2 байта. Получается 4 байта для доступа к любой ячейке памяти.

mov ah,09h
mov dx,offset HelloMsg
int 21h

Тут мы в регистр AH записываем число 09h - номер функции 21-го прерывания, которая выводит строку на экран.

В следующей строке мы в регистр DX записываем адрес(смущение) к началу нашей строки.

Далее мы вызываем прерывание 21h - это прерывание функций DOS. Прерывание - когда выполняющаяся программа прерывается и начинает выполнятся прерывающая программа. По номеру прерывания определяется адрес подпрограммы DOS, которая выводит строку символов на экран.

У вас наверняка возникнет вопрос: А почему мы записываем номер функции 09h в регистр AH ? И почему смещение к строке записываем в регистр DX ?
Ответ простой: для каждой функции определены конкретные регистры, которые содержат входные данные для этой функции. Посмотреть какие регистры нужны конкретным функциям вы можете в help"е.

mov ax,4C00h
int 21h

mov ax,4C00h - пересылаем номер функции в регистр AX. Функция 4C00h - выход из программы.

int 21h - выполняем прерывание (собственно выходим)

end - конец программы.

После директивы end компилятор всё игнорирует, поэтому можете там писать всё, что угодно:)

Если вы дочитали до конца, то вы герой!

Майко Г.В. Ассемблер для IBM PC: - М.: "Бизнес-Информ", "Сирин" 1999 г. - 212 с.

В статье будут рассмотрены основы языка ассемблер применительно к архитектуре win32. Он представляет собой символическую запись машинных кодов. В любой электронно-вычислительной машине самым низким уровнем является аппаратный. Здесь управление процессами происходит командами или инструкциями на машинном языке. Именно в этой области ассемблеру предназначено работать.

Программирование на ассемблер

Написание программы на ассемблере - крайне трудный и затратный процесс. Чтобы создать эффективный алгоритм, необходимо глубокое понимание работы ЭВМ, знание деталей команд, а также повышенное внимание и аккуратность. Эффективность - это критический параметр для программирования на ассемблер.

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

Регистры

Регистрами в языке ассемблер называют ячейки памяти, расположенные непосредственно на кристалле с АЛУ (процессор). Особенностью этого типа памяти является скорость обращения к ней, которая значительно быстрее оперативной памяти ЭВМ. Она также называется сверхбыстрой оперативной памятью (СОЗУ или SRAM).

Существуют следующие виды регистров:

  1. Регистры общего назначения (РОН).
  2. Флаги.
  3. Указатель команд.
  4. Регистры сегментов.

Есть 8 регистров общего назначения, каждый размером в 32 бита.

Доступ к регистрам EAX, ECX, EDX, EBX может осуществляться в 32-битовом режиме, 16-битовом - AX, BX, CX, DX, а также 8-битовом - AH и AL, BH и BL и т. д.

Буква "E" в названиях регистров означает Extended (расширенный). Сами имена же связаны с их названиями на английском:

  • Accumulator register (AX) - для арифметических операций.
  • Counter register (CX) - для сдвигов и циклов.
  • Data register (DX) - для арифметических операций и операций ввода/вывода.
  • Base register (BX) - для указателя на данные.
  • Stack Pointer register (SP) - для указателя вершины стека.
  • Stack Base Pointer register (BP) - для индикатора основания стека.
  • Source Index register (SI) - для указателя отправителя (источника).
  • Destination Index register (DI) - для получателя.

Специализация РОН языка ассемблер является условной. Их можно использовать в любых операциях. Однако некоторые команды способны применять только определенные регистры. Например, команды цикла используют ESX для хранения значения счетчика.

Регистр флагов. Под этим подразумевается байт, который может принимать значения 0 и 1. Совокупность всех флагов (их порядка 30) показывают состояние процессора. Примеры флагов: Carry Flag (CF) - Флаг переноса, Overflow Flag (OF) - переполнения, Nested Flag (NT) - флаг вложенности задач и многие другие. Флаги делятся на 3 группы: состояние, управление и системные.


Указатель команд (EIP - Instruction Pointer). Данный регистр содержит адрес инструкции, которая должна быть выполнена следующей, если нет иных условий.

Регистры сегментов (CS, DS, SS, ES, FS, GS). Их наличие в ассемблере продиктовано особым управлением оперативной памятью, чтобы увеличить ее использование в программах. Благодаря им можно было управлять памятью размером до 4 Гб. В архитектуре Win32 необходимость в сегментах отпала, но названия регистров сохранились и используются по-другому.

Стек

Это область памяти, выделенная для работы процедур. Особенность стека заключается в том, что последние данные, записанные в него, доступны для чтения первыми. Или иными словами: первые записи стека извлекаются последними. Представить этот процесс себе можно в качестве башни из шашек. Чтобы достать шашку (нижнюю шашку в основание башни или любую в середине) нужно сначала снять все, которые лежат сверху. И, соответственно, последняя положенная на башню шашка, при разборе башни снимается первой. Такой принцип организации памяти и работы с ней продиктован ее экономией. Стек постоянно очищается и в каждый момент времени одна процедура использует его.


Идентификаторы, целые числа, символы, комментарии, эквивалентность

Идентификатор в языке программирования ассемблер имеет такой же смысл, как и в любом другом. Допускается использование латинских букв, цифр и символов "_", ".", "?", "@", "$". При этом прописные и строчные буквы эквивалентны, а точка может быть только первым символом идентификатора.

Целые числа в ассемблере можно указывать в системах отсчета с основаниями 2, 8, 10 и 16. Любая другая запись чисел будет рассматриваться компилятором ассемблера в качестве идентификатора.

В записи символьных данных допускается использовать как апострофы, так и кавычки. Если в символьной строке требуется указать один из них, то правила следующие:

  • в строке, заключенной в апострофы, кавычки указываются один раз, апостроф - дважды: "can""t", " he said "to be or not to be" ";
  • для строки, заключенной в кавычки, правило обратное: дублируются кавычки, апострофы указываются как есть: "couldn"t", " My favourite bar is ""Black Cat"" ".

Для указания комментирования в языке ассемблер используется символ точка с запятой - ";". Допустимо использовать комментарии как в начале строк, так и после команды. Заканчивается комментарий переводом строки.

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

Таким образом в программе все вхождения будут заменяться на, на месте которого допустимо указывать целое число, адрес, строку или другое имя. Директива EQU похожа по своей работе на #define в языке С++.

Директивы данных

Языки высокого уровня (C++, Pascal) являются типизированными. То есть, в них используются данные, имеющие определенный тип, имеются функции их обработки и т. д. В языке программирования ассемблер подобного нет. Существует всего 5 директив для определения данных:

  1. DB - Byte: выделить 1 байт под переменную.
  2. DW - Word: выделить 2 байта.
  3. DD - Double word: выделить 4 байта.
  4. DQ - Quad word: выделить 8 байтов.
  5. DT - Ten bytes: выделить 10 байтов под переменную.

Буква D означает Define.

Любая директива может быть использована для объявления любых данных и массивов. Однако для строк рекомендуется использовать DB.

Синтаксис:

В качестве операнда допустимо использовать числа, символы и знак вопрос - "?", обозначающий переменную без инициализации. Рассмотрим примеры:

Real1 DD 12.34 char db "c" ar2 db "123456",0 ; массив из 7 байт num1 db 11001001b ; двоичное число num2 dw 7777o ; восьмеричное число num3 dd -890d ; десятичное число num4 dd 0beah ; шестнадцатеричное число var1 dd ? ; переменная без начального значения ar3 dd 50 dup (0) ; массив из 50 инициализированных эл-тов ar4 dq 5 dup (0, 1, 1.25) ; массив из 15 эл-тов, инициализированный повторами 0, 1 и 1.25


Давно хотел разобраться с этой темой. И вот наконец собрался.

Дело в том, что инструкции процессора Интел и синтаксис вставок ассемблерного кода в программы на Visual C++ не будут работать в Dev-C++ .

Потому что Dev-C++ использует компилятор GCC (бесплатный компилятор языка С++). Этот компилятор имеет встроенный ассемблер, но это не MASM и не TASM с привычным . Это ассемблер AT&T, синтаксис которого очень сильно отличается от синтаксиса MASM/TASM и подобных.

Кроме того, если в Паскале или Visual C++ вы просто используете ключевые слова - операторные скобки (в Паскале это asm...end, в Visual C++ это __asm {...}), и между этими скобками пишите инструкции ассемблера как вы привыкли, то с компилятором GCC это не проканает.

Я сначала никак не мог понять, почему. Но когда немного познакомился с , то понял.

Оказывается, в компиляторе GCC, как и в Паскале и в Visual C++, есть ключевые слова asm и __asm. Вот только это вовсе не операторные скобки!!!

По сути это функции, которые вызываются с определённым набором параметров. И в эти функции в качестве параметров передаются инструкции ассемблера!

Вот уж воистину - зачем просто, если можно сложно!

В общем, использование встроенного ассемблера GCC - это целая наука. Если интересно её освоить, то можете начать вот с (это мой перевод английского оригинала).

А здесь я просто в самых общих чертах покажу, как можно использовать вставки на ассемблере в Dev-C++ (это будет также справедливо для других средств разработки, использующих компилятор GCC).

Ассемблер AT&T

Как я уже сказал, этот ассемблер сильно отличается от привычных нам . Здесь я не буду об этом говорить. Если кому интересно, то основные отличия описаны .

Вставка на ассемблере в Dev-C++

Основной формат вставки кода ассемблера показан ниже:

asm("Здесь код на ассемблере" );

/* помещает содержимое ecx в eax */ asm("movl %ecx %eax"); /* помещает байт из bh в память, на которую указывает eax */ __asm__("movb %bh (%eax)");

Как вы могли заметить, здесь используются два варианта встраивания ассемблера: asm и __asm__. Оба варианта правильные. Следует использовать __asm__, если ключевое слово asm конфликтует с каким-либо участком вашей программы (например, в вашей программе есть переменная с именем asm).

Если встраивание кода на ассемблере содержит более одной инструкции, то мы пишем по одной инструкции в строке в двойных кавычках, а также суффикс ’\n’ и ’\t’ для каждой инструкции.

Asm__ ("movl %eax, %ebx\n\t" "movl $56, %esi\n\t" "movl %ecx, $label(%edx,%ebx,$4)\n\t" "movb %ah, (%ebx)");

Однако в большинстве случаев требуется обмен данными между кодом на ассемблере и переменными, которые объявлены в исходных кодах на языке высокого уровня.

Это тоже возможно. Общий формат ассемблерной вставки для компилятора GCC такой:

Asm (assembler template: output operands /* не обязательно */ : input operands /* не обязательно */ : list of clobbered registers /* не обязательно */);

Не буду здесь подробно всё это расписывать, так как это уже сделано . Там же вы найдёте все подробности использования встроенного ассемблера компилятора GCC (ну хотя не все, а основные).

Я же здесь приведу пример, и на этом успокоюсь.

Для начала не очень хороший пример.

Int x = 0, y = 0; cout

Не очень хороший он потому, что мы изменяем значения регистров, а потом получаем значение регистра eax в переменную х. Но при этом мы не заботимся о том, что состояние этих регистров может быть изменено где-то в другом месте. Так что это может привести к потенциальным неожиданностям.

Теперь попробуем сделать всё чуть более правильно (хотя и не идеально).

Int y = 15, z = 10; cout

Здесь в ассемблерный код мы передаём значения переменных y и z. Значение у помещается в регистр еах (на это указывает буква “a”), а значение z помещается в регистр ebx (на это указывает буква “b”).

Сам ассемблерный код выполняет сложение значений регистров eax и ebx, и помещает результат в eax. А уже этот результат выводится в переменную y. То, что у - это выходная переменная, определяет модификатор “=”.

Ну вот как-то так. Это, конечно, в самых общих чертах. Если кого интересуют подробности, то см. .

6 ответов

Вы можете получить доступ к переменным по их имени и скопировать их в регистры. Вот пример из MSDN:

Int power2(int num, int power) { __asm { mov eax, num ; Get first argument mov ecx, power ; Get second argument shl eax, cl ; EAX = EAX * (2 to the power of CL) } // Return with result in EAX }

Компилятор microsoft очень плохо оптимизируется при подключении встроенной сборки. Он должен создавать резервные копии регистров, потому что если вы используете eax, тогда он не переместит eax в другой свободный регистр, он будет продолжать использовать eax. Ассемблер GCC намного продвинулся вперед на этом фронте.

Чтобы обойти это, Microsoft предложила intrinsics . Это гораздо лучший способ сделать вашу оптимизацию, поскольку она позволяет компилятору работать с вами. Поскольку Крис упоминал, что встроенная сборка не работает под x64 с компилятором MS, поэтому на этой платформе вы ДЕЙСТВИТЕЛЬНО лучше просто используете встроенные средства.

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

В реестрах ничего нет. как выполняется блок _asm. Вам нужно переместить материал в регистры. Если есть переменная: "a", тогда вам нужно

Asm { mov eax, [a] }

Стоит отметить, что VS2010 поставляется с ассемблером Microsoft. Щелкните правой кнопкой мыши по проекту, перейдите к правилам сборки и включите правила сборки ассемблера, а среда IDE обработает файлы.asm.

это несколько лучшее решение, так как VS2010 поддерживает 32-битные и 64-битные проекты, а ключевое слово __asm ​​НЕ работает в 64-битных сборках. Вы ДОЛЖНЫ использовать внешний ассемблер для 64-битного кода:/

Я предпочитаю писать целые функции в сборке, а не с помощью сборки inline . Это позволяет поменять языковые функции высокого уровня на сборку в процессе сборки. Кроме того, вам не нужно беспокоиться о том, что оптимизация компилятора мешает.

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

Если вам нужно вставить встроенную сборку для большой функции, создайте новую функцию для кода, который необходим для встроенного. Снова замените на С++ или сборку во время сборки.

Это мои предложения, ваш пробег май-Вар (YMMV).

Мне очень нравится сборка, поэтому я не собираюсь быть здесь. Похоже, что вы профилировали свой код и нашли "горячую точку", что является правильным способом запуска. Я также предполагаю, что 200 строк, о которых идет речь, не используют много конструкций высокого уровня, таких как vector .

Все, что сказал: если бы я был вами, я бы прошел через этот код в отладчике VS, используя представление "Разборка". Если вам комфортно читать код, когда вы идете вперед, это хороший знак. После этого выполните компиляцию Release (Debug отключит оптимизацию) и создайте список ASM для этого модуля. Тогда, если вы думаете, что видите место для улучшения... у вас есть место для начала. Ответы других людей связаны с документацией MSDN, которая действительно очень скудная, но все же разумное начало.

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

На Си или на Ассемблере . Выбор не прост, не зря программисты однокристальщики раскололись на два непримиримых лагеря. Одни с пеной у рта доказывают, что те кто пишут на Си лохи изнеженные, а настоящий брутальный кодер должен воспринимать только ассемблер. Другие же, усераясь, доказывают, что на ассемблере разве что лампочками помигать можно, а какой либо серьезный проект делать на низком уровне невозможно.

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

Assembler+
Парадоксально, но под микроконтроллеры писать на ассемблере ничуть не сложней чем на Си. В самом деле, программирование контроллера неотделимо от его железа. А когда пишешь на ассемблере, то обращаешься со всей периферией контроллера напрямую, видишь что происходит и как это происходит. Главное, что при этом ты четко понимаешь что, как и зачем ты делаешь . Очень легко отлаживать и работа программы как на ладони.
Да и сам ассемблер изучается очень быстро, за считанные дни. Достаточно постоянно иметь перед глазами систему команд и навык программирования алгоритмов. А соответствующее состояние мозга, когда начинаешь мыслить ассемблерными конструкциями наступает очень быстро.

C-
А вот с высокими языками, например с Си, ситуация уже куда хуже. Да, повторить какой нибудь пример из обучалки, вроде вывода строки на дисплей, Сях куда проще, подключил нужную библиотеку, набрал стандартную команду и вот тебе результат. Но это только так кажется. На самом деле, стоит чуть вильнуть в сторону, как начинается темный лес. Особенно когда дело начинает касаться адресации разных видов памяти,

Напомню, что у AVR, как и подавляющего числа МК, память разделена на код и данные и адреса у них в разных адресных пространствах о которых стандарт Си ничего не знает, ведь он расчитан на некий универсальный вычислитель. А тут приходится вводить всякие модификаторы, квалификаторы и без яростного курения мануалов разобраться в этих примочках бывает очень сложно. Особенно работа с памятью осложняется тем, что программа то работает в том и другом случае, но вот если неправильно указать тип памяти, то оперативка забьется константами и в дальнейшем программу скрючит в самый неподходящий момент. Вот и найди такой глюк

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

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

В таких случаях обычно открывают листинг и смотрят что там происходит в коде на уровне команд процессора, но вот засада — без знания ассемблера там делать нечего! Зато если понимаешь то как работает программа на уровне команд, то найти багу вроде срыва стека или нарушения атомарного доступа проще простого.

Ну и такие гадкие вещи как overhead . Программа на Си требует значительно больше оперативной памяти, значительно больше места во flash памяти , работает куда медленней чем ассемблерная программа. И если на обычном компьютере с его гигагерцами частот, гигабайтами оперативки и дисковой памяти это не столь критично, то вот для контроллера с жалкими килобайтами флеша, у которого частота порой не превышает 16 мегагерц, а оперативки и килобайта не наберется, такой расход ресурсов более чем критичен.

Кроме того существуют такие контроллеры как ATTiny 1x у которых либо вообще нет оперативки, либо она такая мизерная, что даже стек там сделан аппаратным. Так что на Си там ничего написать в принципе нельзя.

Assembler+
Представь, что ты прораб, а компилятор это банда джамшутов. И вот надо проделать дырку в стене. Даешь ты джамшутам отвертку и говоришь — ковыряйте. Проковряют они отверткой бетонную стену? Конечно проковыряют, вопрос лишь времени и прочности отвертки. Отвертка сточится (читай памяти процессора или быстродействия не хватит)? Не беда — дадим джамшутам отвертку побольше, благо разница в цене между большой отверткой и маленькой копеечная. В самом деле, зачем прорабу, руководителю, знать такие низкоуровневые тонкости, как прочность и толщина бетонной стены, типы инструмента. Главное дать задание и проконтроллировать выполнение, а джамшуты все сделают сами.
Задача решается? Да! Эффективно это решение? Совершенно нет! А почему? А потому что прораб не знал, что бетон твердый и отверткой его проковырять сложно. А будь прораб сам когда то рабочим, пусть даже не профи, но своими руками положил плитку, посверлил дырки, то впредь таких идиотских заданий бы не давал. Конечно, нашего прораба можно и в шараге выучить, дав ему всю теорию строения стен, инструмента, материалов. Но ты представь сколько это сухой теории придется перелопатить, чтобы чутье было интуитивным, на уровне спинного мозга? Проще дать в руки инструмент и отправить сверлить стены. Практика — лучший учитель.

Также и с ассемблером. Хочешь писать эффективные программы на высокоуровневом языке — изучи хотя бы один ассемблер, попиши на нем немного. Чтобы потом, глядя на любую Сишную строку, представлять себе во что это в итоге компилируется и как обрабатывается контроллером. Очень помогает в отладке и написании, а уж про ревесирование чужих программ я вообще не говорю.

Assembler-
Но засиживаться в ассемблерщиках и стараться все сделать на нем далеко не обязательно. Ассемблерный код тяжелей переносить с контроллера на контроллер, в нем при переносе можно наделать много незначительных, но тем не менее фатальных ошибок. Отладка которых занимает много времени. Большие проекты писать на ассемблере то еще развлечение. На ассемблере трудно сделать полноценную библиотеку, подключаемую куда угодно. Ассемблер жестко привязан к конкретному семейству контроллеров.

С+
Си хорош за счет огромного числа готового кода, который можно очень легко и удобно подключать и использовать в своих нуждах. За большую читабельность алгоритмов. За возможность взять и перетащить код, например, с AVR на ARM без особых заморочек. Или с AVR на PIC. Разумеется для этого надо уметь ПРАВИЛЬНО писать на Си, выделяя все аппаратно зависимые части в HAL .

Общий расклад примерно таков, что использование высокоуровневого языка на контроллерах с обьемом памяти меньше 8 килобайт является избыточным. Тут эффективней писать все на Ассемблере. Особенно если проект подразумевает не просто мигание светодиодом.
8-16 килобайт тут уже зависит от задачи, а вот пытаться писать на ассемблере прошивку более 16 килобайт можно, но это напоминает прокладку тоннеля под Ла Маншем с помощью зубила.

В общем, знать надо и то и другое. Настоятельно тебе рекомендую начать изучать МК с ассемблера. А как только поймешь, что на асме можешь реализовать все что угодно, любой алгоритм. Когда досконально прочувствуешь работу стека, прерываний, организацию переходов и ветвлений. Когда разные трюки и хитрости, вроде игр с адресами возврата из прерываний и процедур, переходами и конечными автоматами на таблицах и всякие извраты будут вызывать лишь интерес, но никак не взрыв мозга из серии «Аааа как это работает??? Не понимаю?!!»
Вот тогда и можно изучать Си. Причем, изучать его с дебагером в руках. Не просто изучить синтаксис (там то как раз все элементарно), а понять ЧТО и КАК делает компилятор из твоего исходника. Поржать над его тупостью или наоборот поудивляться извратам искуственного интелекта. Понять как компилятор делает ветвления, как организует циклы, как идет работа с разными типами данных, как ведется оптимизация. Где ему лучше помочь, написав в ассемблерном стиле, а где не критично и можно во всю ширь использовать языковые возможности Си.

А вот начать изучение ассемблера после Си мало кому удается. Си расслабляет, становится лень и впадлу. Скомпилировалось? Работает? Ну и ладно. А то что там быдлокод, та пофигу… =)

А как же бейсик, паскаль и прочие языки? Они тоже есть на AVR?
Конечно есть, например BascomAVR или MicroPASCAL и во многих случаях там все проще и приятней. Не стоит прельщаться видимой простотой. Она же обернется тем, что потом все равно придется переходить на Си.

Дело в том, что мир микроконтроллеров далеко не ограничивается одним семейством. Постоянно появляются новые виды контроллеров, развиваются новые семейства. Ведь кроме AVR есть еще и ARM, PIC, STM8 и еще куча прекрасных контроллеров со своими плюсами.
И под каждый из этих семейств есть Си компилятор. Ведь Си это, по сути, промышленный стандарт. Он есть везде и контроллер который не имеет под него компилятора популярным у профессионалов не станет никогда.

А вот на бейсик с паскалем, обычно, всем пофигу. Если на AVR и PIC эти компиляторы и сделали, то лишь потому, что процы эти стали особо популярны у любителей и там наверняка найдется тот, кто заинтересуется и бейсиками всякими. С другим семейством контроллеров далеко не факт, что будет также радужно. Например под STM8 или Cortex M3 я видел Pascal в лучшем случае только в виде кривых студенческих поделок. Никак не тянущих на нормальный компилятор.

Такой разный Си
С Си тоже не все гладко. Тут следует избегать компиляторов придумывающих свои диалектные фишки. Например, CodeVision AVR (CVAVR) позволяет обращаться к битам порта с помощью такого кода:

PORTB |= 1<<7;

Использование диалектов не позволит тебе скакать с компилятора на компилятор. Таскать код повсюду копипастом. Привязывает к одному конкретному компилятору и далеко не факт, что он окажется хорошим и будет поддерживать все новые контроллеры семейства.

Приплюснутый
Некоторое время назад я считал, что С++ в программировании микроконтроллеров не место. Слишком большой overhead. C тех пор мое мнение несколько поменялось.
Мне показали очень красивый кусок кода на С++, который компилился вообще во что то феерическое. Компактней и быстрей я бы и на ассемблере не факт что написал. А уж про читабельность и конфигурируемость и говорить не приходится. Все из знакомых программистов, кто видел этот код, говорили что-то вроде «Черт, а я то думал, что я знаю С++».

Так что писать на С++ можно! Но сделать компактный и быстрый код на С++ чертовски виртуозная задача, надо знать этот самый С++ в совершенстве. Судя по тому, что столь качественных примеров эффективности С++ при программировании под МК мне попадалось раз-два, то видимо пороговый уровень входа в эту область весьма и весьма велик.

В общем, как говорил Джон Кармак, «хороший С++ код лучше чем хороший С код. Но плохой С++ может быть намного ужасней чем плохой С код».




Top