1. В сообществе нашего форума Вконтакте создан раздел по продаже электронных компонентов.
    Каждый может продать в нем свои залежавшиеся детали. Подробности здесь.

Туториал Ассемблер с самого нуля.

Тема в разделе "Программирование", создана пользователем UL7AAjr, 18 фев 2015.

  1. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    В этой теме я попробую рассказать об Ассемблере в общих чертах, не привязываясь к конкретному МК. Постараюсь простыми словами, иногда буду упрощать, чтобы мог и ребенок при желании понять.

    В общем попробую. Учитель из меня не очень, но может быть опыт заменит преподавательские навыки:)
    monitorrr нравится это.
     
    : asm, туториал
  2. DenisD

    DenisD В доску свой

    Сообщения:
    354
    Симпатии:
    63
    Тема нужная! Нам в политехе преподавали немного его, было интересно, но мало и никакой практики.
  3. monitorrr

    monitorrr В доску свой

    Сообщения:
    331
    Симпатии:
    94
    Род занятий:
    инженер сервиса
    Адрес:
    Алматы
    Подписался, жду!
  4. Чапаев

    Чапаев В доску свой

    Сообщения:
    182
    Симпатии:
    20
    Род занятий:
    Охранные системы безопасности
    Адрес:
    Астана
    Ассемблер под STM8 было бы очень замечательно почитать.
  5. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Ну… начнем. Попробую описать принципы работы с ассемблером для восьмиразрядных МК.

    У ассемблеров для восьмиразрядных МК есть своя специфика, несколько отличная от ассемблеров для «старших» процессоров. Сначала давайте коротко пробежимся по собственно объекту программирования, то-бишь восьмиразрядному МК. Я не буду вдаваться в детали, а остановлюсь только на важных, с точки зрения программирования, моментах.

    Можно сказать, что большинство 8-разрядных МК построены по более-менее одинаковому принципу. Все они имеют три главных компонента: АЛУ (арифметико-логическое устройство), память и периферийные устройства.

    Что нужно знать о каждом из этих пунктов.

    Память. Представляет из себя длинную последовательность байт. Номер байта мы называем адресом в памяти. Нумерация начинается с «0». Обычно в МК память разбита на блоки трех типов: RAM– оперативная память, хранит текущие данные и результаты вычислений, при выключении питания данные этой памяти не сохраняются. ROM (Flash) в памяти этого типа располагается программный код, эта память перезаписываемая и сохраняет данные при выключении питания. Когда мы говорим «прошить МК», под этим как раз подразумевается записать данные в ROM (Flash) память. Третий (но не последний) тип памяти с которым мы столкнемся – EEPROM. Здесь хранятся различные настройки и калибровки. Эта память сохраняет свои данные при отключении питания и может быть записана в момент выполнения программы.

    АЛУ. Собственно мозг МК. Интерпретирует и выполняет команды записанные в ROM. Как раз от его конструкции и зависит набор исполняемых команд, и соответственно Ассемблер.

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

    Вот так вот примерно можно представить память МК.

    Untitled-1.jpg



    Ну… собственно с объектом программирования думаю и так всем было понятно. Со вступлением покончено. Теперь, пожалуй, дело за самим Ассемблером :)
    Lavad, FreshMan, Danil и ещё 1-му нравится это.
  6. monitorrr

    monitorrr В доску свой

    Сообщения:
    331
    Симпатии:
    94
    Род занятий:
    инженер сервиса
    Адрес:
    Алматы
  7. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Как было сказано выше – АЛУ – мозг МК, который выполняет последовательность инструкций, которую мы называем программой. Сами инструкции представляют собой шестнадцатеричные числа, и, согласитесь, вот так писать было бы неудобно: 21 00 00 90 90 C9 …, хотя и возможно. Для написания инструкций АЛУ в виде удобном и понятном для человека и придумали Ассемблер.

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

    Предположим, что имеется записанная в памяти программа и АЛУ, ее исполняющее. АЛУ соединено с памятью двумя шинами: адреса и данных. Кстати, размерность шины данных как раз определяет разрядность процессора. Восьмибитный процессор означает, что его шина данных, соединяющая АЛУ с памятью, шириной 8 бит (один байт). В то время как шина адреса может иметь другую разрядность. Исходя из такой топологии, можно понять, что за один прием, АЛУ может обратиться только к одному байту памяти. Как это влияет на программирование? А получается так, что обычное присвоение одной ячейки памяти другой выполняется в два приема: 1 – прочитать один адрес памяти, 2 – записать другой адрес памяти. Соответственно и инструкций будет две. Поэтому не надо удивляться, что все, что вы делали на языке высокого уровня в одной строке, может превратиться в небольшой текст на Ассемблере. Тут еще есть момент, что при выполнении операции присвоения в два приема, результат загрузки данных надо где-то запомнить, а во второй операции, записать запомненный результат. Так вот, очень важный элемент, это то, куда АЛУ временно забирает данные из памяти и это есть регистры, внутренняя память АЛУ. Именно над этими регистрами производятся арифметические, логические и другие операции.

    Теперь думаю понятно, как АЛУ будет выполнять, например: V1 = V2 + K;

    1. Загрузить V2 в первый регистр

    2. Загрузить K во второй регистр

    3. Сложить первый и второй регистр (результат в первом регистре)

    4. Выгрузить первый регистр в V1

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

    1. Загрузить V2 в первый регистр

    2. Добавить к первому регистру содержимое памяти по адресу K (выставить на шину адреса адрес K и использовать шину данных как регистр)

    3. Выгрузить первый регистр в V1

    Каждую из этих строк можно записать на Ассемблере и это будет работать. Но не все сразу. Из этого примера видно, что были использованы инструкции двух типов: загрузки-выгрузки (пересылки) и сложения (арифметическая операция). Давайте посмотрим, какие еще типы операций может выполнять АЛУ.


    Основные группы инструкций АЛУ (ну и соответственно Ассемблера).

    1. Пересылки данных

    2. Арифметические.

    3. Логические

    4. Управления ходом выполнения.

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

    продолжение следует....
    Danil, DenisD и monitorrr нравится это.
  8. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Немного отвлечемся от «голой» теории, и, чтобы не скучать, сделаем перерыв на небольшую практику. Ну и забегая вперед посмотрим, как будет выглядеть «классическая» программа моргания светодиодом. Итак, как это сделать.

    1. Настроить ножку порта, к которому подключен светодиод, на выход.​

    2. Переключить состояние светодиода (если был включен – выключить, если был выключен – включить)​

    3. Подождать какое-то время​

    4. Перейти к пункту 2.​

    Ничего сложного, правильно? Теперь как реализовать каждый пункт. Предположим, что светодиод подключен к 3-му биту порта B.

    Вариант для AVR.

    1. Настройка порта на выход. Для настройки порта на выход, Нужно установить в "1" соответствующий бит порта DDRx. (DDR – Data Direction Register/регистр направления, x – порт, в нашем случае B). Получается, что нужно установить в «1» третий бит порта DDRB. Эта инструкция на ассемблере выглядит вот так


    SBI DDRB, 3


    где

    SBI – Set Bit in I/O register
    DDRB– порт, определяющий направление ножек для порта B
    3 – мы хотим установить в «1» третий бит​



    2. Переключить состояние светодиода. По сути нам нужно сменить значение третьей ножки порта В на противоположное. В этом МК нет инструкции, позволяющей инвертировать значение определенного бита порта, но есть способ инвертировать значение определенного бита регистра. Значит нужно загрузить в регистр значение порта В, инвертировать третий бит в регистре и выгрузить регистр назад в порт В. Будем использовать регистр R0.

    IN R0, PORTB
    LDI R16, $08
    EOR R0, R16
    OUT PORTB, R0

    где

    IN R0, PORTB - загрузка значения порта В в регистр R0​

    LDI R16, $08 – вспомогательная операция, загружаем в регистр R1 маску инвертирования бит , $08 = 0000 1000 в битовом представлении, 1 взведена в третьем разряде, мы собираемся инвертировать третий бит​


    EOR R0, R16 – выполняем операцию «исключающее ИЛИ» данных регистра R0 (содержимое порта В) и регистра R16 (маска инвертирования). Эта операция инвертирует те биты в регистре R0, которые установлены в «1» у регистра R16.​


    OUT PORTB, R0 – положить измененные данные из регистра R0 в порт В.​


    3. Подождать какое-то время. Тут есть масса вариантов. Можно просто занять процессор чем-нибудь ненужным. Например вычитать единицу из большого числа, пока число не превратится в «0».


    LDI R17, $FF
    one_more:
    DEC R17
    BRNE one_more

    где

    LDI R17, $FF– загрузить в регистр R17 большое число $FF (255). Число назвать большим конечно нельзя, но так как регистр всего 8-ми битный, то больше число туда просто не влезет. Почему R17? Прсосто любой неиспользуемый регистр​


    one_more: - это метка, так мы обозначаем (метим) определенную инструкцию, если ее адрес в дальнейшем понадобится. В данном случае мы пометили инструкцию DEC R17, так как позже нужно будет перейти опять на эту инструкцию​


    DEC R17 – уменьшить значение регистра R17 (вычитается единица).​

    BRNE one_more - если результат предыдущей операции не нулевой, то передать управление (перейти) на метку one_more. Имя метки можно выбрать другое, не имеет значения, но лучше давать меткам более-менее «самоговорящие» имена.​

    Вообще пара инструкций DEC BRNE используется очень часто при организации циклов, т.е. петли в коде программы. В данном случае мы организовали цикл, который «крутится» до тех пор, пока результат DEC R17 не станет нулевым. Таким образом мы заставляем процессор задержаться в этом месте. После окончания цикла (результат DEC R17 станет нулевым) выполнение программы продолжится. Реально этот цикл выполнится просто мгновенно, и частота мигания светодиода будет столь высокой, что для глаза не заметно. Ну это ведь для примера:)



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


    RJMP flash_more

    В итоге программа на Ассемблере для AVR будет выглядеть вот так
    Untitled-3.jpg

    Продолжение следует...
    Последнее редактирование: 22 фев 2015
    Lavad, Buba_Chkhadze и monitorrr нравится это.
  9. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    В "догонку" к предыдущему посту. Как будет выглядеть та-же программа на другом Ассемблере. В данном случае это Ассемблер для STM8.


    Untitled-1.jpg

    Здесь видно, что способ записи аналогичных инструкций уже другой. Для инвертирования бита #3 в порту PB используется одна инструкция. Цикл организован уже до FFFF, так как для цикла использован адресный регистр X.
    monitorrr нравится это.
  10. _VN_

    _VN_ В доску свой

    Сообщения:
    468
    Симпатии:
    58
    Род занятий:
    Инженер
    Адрес:
    Алматы
    К каждому типу контроллера, по крайней мере для Atmel, выпускается файл типа tn15def.inc, в котором ВСЕ функции и регистры контроллера описываются в стандартизованном для этого семейства виде. Программа ассемблера и указанный файл связаны между собой на логическом и аппаратном уровне. Своеобразный микросправочник по ассемблеру для выбранного микроконтроллера. Я изменил наименование файла .inc на .txt для загрузки его на сервер.

    Вложения:

    • tn15def.txt
      Размер файла:
      12,8 КБ
      Просмотров:
      24
    Последнее редактирование: 23 фев 2015
  11. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    И какая польза в данный момент от этого файла? Покажите мне хоть одну функцию описанную в этом файле. Вроде-бы там ВСЕ должны быть. Где хоть какое-то описание в "стандартизованном виде" регистров (например регистра указателя стека или регистра статуса)? Если это "микросправочник по ассемблеру", то где-же там описание хоть каких-то инструкций Ассемблера?

    PS: Вероятно при переименовании .inc в .txt все потерялось:)
  12. _VN_

    _VN_ В доску свой

    Сообщения:
    468
    Симпатии:
    58
    Род занятий:
    Инженер
    Адрес:
    Алматы
    Не потерялось... Этот файл для МК ATtiny15, самый "простой" из этой серии. Регистра указателя стека в этом контроллере просто нет. В МК этого семейства стек организован аппаратно, имеет три уровня и недоступен для программиста. МК сам управляет перемещением данных по стеку.
  13. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    А где-же, за исключением регистра стека, описание всех остальных регистров? Их тоже нет что-ли в этом МК или они недоступны для программиста? Где-же ВСЕ обещанные функции, микросправочник по Ассемблеру? Вы там ничего не попутали прежде чем написать все это?
  14. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Ладно... дабы не сбивать всех с толку, объясню, что находится в пресловутом файле.

    На Ассемблере можно записать не только инструкции для МК, но и некоторые директивы управляющие процессом компиляции. Например директива EQU. Ее работу можно представить как инструкцию подстановки. Допустим мы имеем инструкцию "иди в сад" и задаем директиву:
    .equ сад зад
    В этом случае Ассемблер, перед компиляцией, сначала заменит "сад" на "зад", и затем выполнит компиляцию инструкции "иди в зад". Т.е. предварительно произведет подстановку.

    Зачем это нужно. Дело в том, что у разных моделей МК логически одинаковые регистры могут располагаться по разным адресам. Мы используем символические имена регистров, например PORTB, но физически он может имеет разный адрес, в зависимости от модели МК. Так как мы обращаемся к регистру по имени, а Ассемблеру нужно знать физический адрес этого регистра для компиляции, используется файл определений. Такой файл содержит собранные воедино подстановки для конкретной модели МК. Ну и другие необходимые Ассемблеру директивы, специфичные для данной модели МК.
  15. radioengineer

    radioengineer Администратор Команда форума

    Сообщения:
    3.489
    Симпатии:
    345
    Адрес:
    Алматы
    Типа как в Си #define?
  16. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    Почти так. Директива DEFINE в Ассемблере тоже есть, хоть она и похожа по действию на EQU, но также используется для определения условной компиляции (IFDEF - ELSE). А вот EQU - просто подстановка.
  17. _VN_

    _VN_ В доску свой

    Сообщения:
    468
    Симпатии:
    58
    Род занятий:
    Инженер
    Адрес:
    Алматы
    Сервер принял только этот файл. Остальные оказались просто большими по размеру...

    Вложения:

    • inst_set.pdf
      Размер файла:
      182,9 КБ
      Просмотров:
      13
  18. UL7AAjr

    UL7AAjr В доску свой

    Сообщения:
    1.895
    Симпатии:
    310
    Род занятий:
    инженер-программист
    Адрес:
    Алма-Ата
    И он оказался просто большим по размеру. Ну бывает такая незадача:)

    PS: почему-то в последнем файле тоже нет ВСЕХ функций. Наверное опять слишком большой оказался.
  19. _VN_

    _VN_ В доску свой

    Сообщения:
    468
    Симпатии:
    58
    Род занятий:
    Инженер
    Адрес:
    Алматы
    Это так... На самом деле полная версия этого файла "весит" 1.5 Mb, именуется "Atmel-0856-AVR-Instruction-Set-Manual.pdf" и находится по адресу http://www.atmel.com/devices/atmega8a.aspx?tab=documents.
    Это самая настоящая инструкция по эксплуатации описывает применение каждой команды на Ассемблере или на Си.
  20. Buba_Chkhadze

    Buba_Chkhadze Модератор Команда форума

    Сообщения:
    4.354
    Симпатии:
    326
    Адрес:
    Талгар
    продолжение будет ?

Поделиться этой страницей