AVR. Учебный курс. Макроассемблер

Распечатать

Перед изучением системы команд микроконтроллера надо бы разобраться в инструментарии. Плох тот плотник который не знает свой топор. Основным инструментом у нас будет компилятор. У компилятора есть свой язык — макроассемблер, с помощью которого жизнь программиста упрощается в разы. Ведь гораздо проще писать и оперировать в голове командами типа MOV Counter,Default_Count вместо MOV R17,R16 и помнить что у нас R17 значит Counter, а R16 это Default_Count. Все подстановки с человеческого языка на машинный, а также многое другое делается средствами препроцессора компилятора. Его мы сейчас и рассмотрим.

Комментарии в тексте программы начинаются либо знаком «;«, либо двойными слешами «//«, а еще AVR Studio поддерживает Cишную нотацию комментариев, где коменты ограничены «колючей проволокой» /* коммент */.

Оператор .include позволяет подключать в тело твоей программы кусок кода из другого текстового файла. Что позволяет разбить большую исходник на кучу мелких, чтобы не загромождать и не мотать туда сюда огромную портянку кода. Считай куда ты воткнул .include туда и вставился кусок кода из другого файла. Если надо подключать не весь файл, а только его часть, то тебе поможет директива .exit дойдя до которой компилятор выйдет из файла.

Оператор .def позволяет привязать к любому слову любое значение из ресурсов контроллера — порт или регистр. Например сделал я счетчик, а считаемое значение находится в регистре R0, а в качестве регистра-помойки для промежуточных данных я заюзал R16. Чтобы не запутаться и помнить, что в каком регистре у меня задумано я присваиваю им через .def символические имена.

1
2
.def 	schetchik = R0
.def 	pomoika = R16

И теперь в коде могу смело использовать вместо официального имени R0 неофицальную кличку schetchik
Одному и тому же регистру можно давать кучу имен одновременно и на все он будет честно откликаться.

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

1
.undef 	pomoika

Оператор .equ это присвоение выражения или константы какой либо символической метке.
Например, у меня есть константа которая часто используется. Можно, конечно, каждый раз писать ее в коде, но вдруг окажется, что константа выбрана неверно, а значит придется весь код шерстить и везде править, а если где-нибудь забудешь, то получишь такую махровую багу, что задолбаешься потом ее вылавливать. Так что нафиг, все константы писать надо через
.equ! Кроме того, можно же присвоить не константу, а целое выражение. Которое при компиляции посчитается препроцессором, а в код пойдет уже исходное значение. Надо только учитывать, что деление тут исключительно целочисленное. С отбрасыванием дробной части, без какого-либо округления, а значит 1/2 = 0, а 5/2 = 2

1
2
3
.equ 	Time = 5
.equ 	Acсelerate = 4
.equ 	Half_Speed = (Accelerate*Time)/2

Директивы сегментации. Как я уже рассказывал в посте про архитектуру контроллера AVR память контроллера разбита на независимые сегменты — данные (ОЗУ), код (FLASH), EEPROM

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

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

В сегменте кода уместны директивы:
Адресная метка. Любое слово, не содержащее пробелов и не начинающееся с цифры, главное, чтобы после него стояло двоеточие.

1
2
3
.CSEG
label:   LDI      R16,'A'
         RJMP   label

В итоге, после компиляции вместо label в код подставится адрес команды перед которой стоит эта самая метка, в данном случае адрес команды LDI R16,’A’
Адресными метками можно адресовать не только код, но и данные, записанные в любом сегменте памяти. Об этом чуть ниже.

.ORG address означет примерно следующее «копать отсюда и до обеда», т.е. до конца памяти. Данный оператор указывает с какого адреса пойдет собственно программа. Обычно используется для создания таблицы прерываний.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
	.CSEG
	.ORG 0x0000		
	RJMP Start  ;перепрыгиваем таблицу векторов.
 
	.ORG INT0addr 	; External Interrupt0 Vector Address
	RJMP INT0_expection
 
	.ORG INT1addr 	; External Interrupt1 Vector Address
	RETI
 
	.ORG OC2addr 	; Output Compare2 Interrupt Vector Address
	RJMP PWM_1
 
	.ORG OVF2addr 	; Overflow2 Interrupt Vector Address
	RETI
 
	.ORG ICP1addr 	;Input Capture1 Interrupt Vector Address
	RETI
 
	.ORG 0х0032 		; Начало основной программы
 
Start: 	LDI R16,0x54 	; и понеслась

Статичные данные пихаются в флеш посредством операторов

.db массив байтов.
.dw массив слов — два байта.
.dd массив двойных слов — четыре байта
.dq массив четверных слов — восем байт.

1
2
3
Constant:	.db  	10     ; или 0хAh в шестнадцатеричном коде 
Message:	.db  	"Привет лунатикам"
Words: 		.dw	10, 11, 12

В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов фразы «привет лунатикам», а дальше 000A, 000B, 000С.
Последнии числа, хоть сами и невелики, но занимают по два байта каждое, так как обьявлены как .dw.

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

Тут действует оператор .BYTE позволяющий указать на расположение данных в памяти.

1
2
var1: 		.BYTE 1
table: 		.BYTE 10

В первом случае мы указали переменную var1 состоящую из одного байта.
Во втором случае у нас есть цепочка из 10 байт и переменная table указывающая на первый байт из цепочки. Адрес остальных вычисляется смещением.
Указывать размеры перменных нужно для того, чтобы компилятор их правильно адресовал и они не налезали друг на друга.

.EESEG сегмент EEPROM, энергонезависимая память. Можно писать, можно считывать, а при пропаже питания данные не повреждаются.
Тут действуют те же директивы что и в flash — db, dw, dd, dq.

MACRO — оператор макроподстановки. Вот уж реально чумовая вещь. Позволяет присваивать имена целым кускам кода, мало того, еще параметры задавать можно.

1
2
3
4
.MACRO SUBI16		; Start macro definition 
        subi @1,low(@0)	; Subtract low byte 
        sbci @2,high(@0)	; Subtract high byte 
.ENDM                       	; End macro definition

@0, @1, @2 это параметры макроса, они нумеруются тупо по порядку. А при вызове подставляются в код.

Вызов выглядит как обычная команда:

1
SUBI16 	0x1234,r16,r17

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

Макроассемблер это мощнейшая штука. По ходу пьесы я буду вводить разные макросы и показывать примеры работы макроопределений.

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

138 комментариев: AVR. Учебный курс. Макроассемблер

  1. Stebanoid говорит:

    1. Не понял про директиву .ORG Можно поподробнее?
    2. В коде
    Message: .db «Привет инопланетные придурки
    по всей видимости упущена закрывающая кавычка.
    3. Не понтно про
    var1: .BYTE 1 — зачем это нужно? как можно применить?

    4. Самое главное :). Рискуя показаться не в меру наглым, прошу сообщить мне свой e-mail, что бы я вам надоедал всякими вопросами, которые не получается приткнуть ни в одной теме на этом сайте. Просто мне не у кого больше спросить.
    Если не хотите выкладывать свой адрес здесь, можно просто написать мне письмо. Мой адрес: мой_ник@gmail.com
    Обещаю сильно не надоедать. :)

    • Stebanoid говорит:

      Мыло твоё уже нашёл. Буду писать. :)

    • DI HALT говорит:

      директива ORG говорит компилятору — Копать отсюда и до конца.
      Т.е. у тебя память идет последовательно 0..1..2..3..4..5 и так до самого конца. Если ничего не указывать, то компилер команды забьет начиная с нуля. А если сказать, что у нас ORG 5 то уже будут записываться начиная с 5го адреса.

      Да, кавычку забыл. Надо будет поправить.

      Для того, чтобы у тебя в ОЗУ был зарезервирован байт и был адрес на него. А уж что с этим байтом ты будешь делать тебе решать. Я как переменные использую.

  2. skadi.exe говорит:

    отправь куданибудь, где можно толково почитать про обычный ассемблер, т.к. подразумевается, что читатель его уже знает. Желательно попроще, сам 2 курс и знаком только с приплюснутым…

  3. ymn говорит:

    Купил давеча ATmega16-16PU и чёт нигде не могу найти что означает маркировка 16PU…

  4. testicq говорит:

    не понятно с директивой .equ. вот кусок кода, который не работает должным образом:


    ;+Установка времени следующего срабатывания Timer0
    ; Параметры: @0 - число тиков, через которое сработает таймер
    ; Модифицирует: R16
    .MACRO st0of
    outi TCNT0, $FF-@0
    .ENDMACRO

    .equ XTAL = 8000000 ; Частота МК

    .equ t0_divider = 256 ; Предделитель timer0
    .equ impulse_time = 1200; ; Время в микросекундах
    .equ t0_ticks = (XTAL*impulse_time)/(t0_divider*1000000) ; Время в тиках

    st0of t0_ticks ; Задаем время до срабатывания (в тиках)

    если поставить

    st0of 37

    то все работает на ура

    • DI HALT говорит:

      Гхм, может слишком много вложенных макросов?

      У тебя в макросе St0of макрос outi плюс сложная макроподстановка. Попробуй вынести FF-@0 в отдельный EQU

      • DI HALT говорит:

        Да, кстати, результат вычисления какой получается? Целый?

        • testicq говорит:

          неа. дробный, но мне целое число нужно. пофиг как округленное
          В принципе задал значение 1152 вместо 1200 чтобы при любых (>=2MHz) частотах XTAL был целый результат (хочу сделать универсально) — все заработало, но вопрос остался — Как макросредствами делать целочисленное деление?

          • testicq говорит:

            Странно, но вот такой код работает на ура даже с учетом того, что дробное значение получается

            .equ 	XTAL = 8000000		; Частота МК
             
            .equ	one_time = 1800; 	; Время проседания линии при логич. "1" (мс.)
            .equ	zero_time = 600; 	; Время проседания линии при логич. "0" (мс.)
            .equ	check_time = zero_time+(one_time-zero_time)/2; 	; Время проверки переданного бита (мс.)
             
            .equ	t0_divider = 256	; Предделитель timer0
            .equ	t0_check_ticks = XTAL/1000000*check_time/t0_divider ; Время в тиках
             
            outi	TCNT0, Low($FF-t0_check_ticks)
          • DI HALT говорит:

            ХЗ. В Хелпе к студии есть описание макроязыка. По моему там получается как с типом int — т.е. дробная часть просто теряется.

            А ну так тут ты явно привел все к однобайтному результату взяв сразу Low байт и все дела. А там ты пытался напрямую сложить в однобайтный регистр двубайтное (или более) число, вот компилятор и не понял, что ты пытался ему скормить.

  5. skywalker говорит:

    Че то я не совсем понял, чем отличаются директивы .def и .equ

    • DI HALT говорит:

      equ означает что «Теперь вот это слово равно/эквивалентно этому числу и вместо числа может быть слово»
      def означает что «Теперь этот регистр можно обозвать еще и таким словом »

      Регистру мы можем присвоить кучу разных имен и это не будет ошибкой (варнинг будет, но на него можно забить)

      А вот заэквивалентить одному слову несколько чисел уже нельзя (наоборот, разным словам одно число — запросто, без проблем).

  6. Ridik911 говорит:

    а моджете прикрепить в конце статьи http://www.atmel.ru/Articles/Atmel11.htm для более глубоко ознакомления.. или не требуется?

  7. Ridik911 говорит:

    жду инфу про арифметические (ну и про логические операции) на асм
    и если можно то и комбинации арифметических операций например для вычисления синуса (макросом или процедурой\функцией?)
    спасибо

    • DI HALT говорит:

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

      • Ridik911 говорит:

        Вычислять их — дикий расход памяти и быстродействия.
        —-
        ну да.. в ряд тейлора (циклом) до необходимой точность.. это мучительно для проца и памяти МК

        задавать таблицей в томто и дело и неподходит…
        ладно раскужу что хочу

        хочеца сделать чтото типа мидиконтроллер — тоесть контроллер генерирующий звук при подключенной к нему некоторой клавиатуре… -но т.к. миди то нужно хранить семплы гдето например в ееprom- но памяти этой в МК будет очень мало (надо «допаивать» 24хх)
        тогда яже свел задачу к генерации.. формулами — это экономит ROM память но предпожил что процессор потянет вычисления.. я так понял из ваших слов непотянет..
        да и самое сложное в моей идее — это подбор формул легче семплами

        допустим выбрал метод семплы.. а вот нет идеи как их ээ микшировать\сумировать(когда нажаты две и более кнопки на клавиатуре)? (повидимум програмно но как.. прирывания — нет имхо, акакже)

        • DI HALT говорит:

          Ну почему не потянет — потянет, только сожрет много ПЗУ.

          А что из себя представляет сэмпл?

          • Ridik911 говорит:

            впринципе как ты и сказал «проще задавать таблицей»
            впринципе семпл wav файл (а в реале миди семпл я незнаю, но возможно также)
            тоесть мидиконтроллером управляем семплами: изменить скорость воспроизведения(нота), продолжительность, громкость… и т.д.
            но семплы не мало занимают места как уже понятно…
            например если делать семплы wav то каждый будет весить гдето по 4-8КБ

            только сожрет много ПЗУ.
            —-
            может ОЗУ?

            кстати очееень жду статейку про подключение к МК озу
            для тех МК в которых это поддерживается (типа мега8515 ) и для тех кто не поддерживает но можно(если можно) реализовать програмно
            а также про dram (валяется планка сим.. думаю какбы примостырить к МК)

          • Ridik911 говорит:

            впринципе как ты и сказал «проще задавать таблицей»
            —-
            так и называют wavetable

          • Agrin говорит:

            Можно использовать синтезаторы типа YM3812 или аналогичные. Синтезировать качественно многоканальный звук из сэмплов или функционально с помощью 8разрядного МК задача если и решимая, то с качеством звука как из китайских говорящих кукол.

  8. RMiSe говорит:

    Здравствуйте! DI HALT, не могли ли вы написать (может быть макрос) как из переменной table (table:.BYTE 10) считать побайтно символы?
    Заранее благодарен за ответ!

    • DI HALT говорит:

      ПОбайтно? Хм.

      Проще вот так:
      LDS R16,Table ; первый байт
      LDS R16,Table+1 ; второй байт ну и так далее.

      Если надо программно выбирать значение, то тут есть другой подход.

      LDI XL,low(table)
      LDI XH,High(table)

      LD R16,X
      ; считать первый байт или

      LD R16,X+
      ;считать первый байт и после увеличить Х на 1

      Ну или вручную складывать регистровую пару Х с константой-смещением. Вариантов тьма. Что что, а инструментарий работы с ОЗУ у AVR очень богатый.

  9. VladislavZ говорит:

    А можно по подробней об ОПЕРАТОРАХ .equ .def !? К примеру, возможно ли присвоить имя не регистру, а скажем порту или даже лучше конкретному пину порта!? Возможно, ли определенным ИМЕНАМ присвоить значение?!
    Очень хочется статейки по различным интерфейсам, и самое главное про библиотеки, и по подробней!

    • DI HALT говорит:

      можно.

      def работает ТОЛЬКО с регистрами. А вот имена портов это не более чем equ под имя порта, все они прописаны в inc файле соответствующего мк.

      вот у меня обычно такая ботва:
      .def COK = R26
      .def SEDOK = R27
      .def Catch = R28
      .def Armed = R29

      .equ R_ON = 6
      .equ R_ON_P = PORTB
      .equ R_ON_D = DDRB

      .equ R_EN = 7
      .equ R_EN_P = PORTB
      .equ R_EN_D = DDRB

      • VladislavZ говорит:

        То есть получается (.equ R_ON = 6) R_ON 6-ой пин неизвестного пора ; (.equ R_ON_P = PORTB) R_ON_P все пины портаВ. А совместить не как не получится? К примеру, я хочу управлять 4 пином ddrC, 5 пином ddrC по отдельности и каждому присвоить свое имя, допустим scl ddrC,4; sda ddrC,5 и потом уже в коде так

        Sbi sda
        Sbi scl
        Cbi sda
        Sbrc sda

        • DI HALT говорит:

          Не, не получится.

          Только SBI IIC_PORT,SDA

          Но никто не мешает сделать тебе макрос, например:

          .MACRO SBI_SDA
          SBI IIC_PORT,SDA
          .ENDM

          И юзать в своей программе
          SBI_SDA как одну команду. Тока макросы тогда уже удобней звать, например так:

          SDA_H
          SDA_L

  10. Alex_Megavolt_79 говорит:

    Di Halt, я не понял как кусок проги или всю её сделать как макрос и юзать. Например вот я не давно изучал динамическую индикацию, вот как всю её или часть забахать макрос и как задавать параметры то есть заюзать:

    ; Выполняемые функции: Динамическая индикация
    ;__________________________________________________________________________

    .device ATtiny2313
    .nolist
    .include «d:\avr\def\tn2313def.inc»
    .list

    ;=====================================================================
    ; Объявления:

    .def temp1=r1
    .def temp2=r2
    .def n_digit=r3
    .def counter=r4
    .def digit_out=r5
    .def n_point=r6
    .def temp=r16

    .equ crystal=6000000 ;частота кварца
    .equ Sciler=1024 ;предделитель таймера
    .equ N_TC0_2500uc=255-crystal/(Sciler*400);число тиков таймера ТС0 для организации
    ;задержи при динамической индикации
    .equ amount_digit=4 ;маска, нужно ввести количество разрядов индикатора
    ;в данном случае 4-х разрядный индикатор

    ;======================================================================
    ; Начало программы:

    .dseg

    Digit: .byte 4
    Point: .byte 1

    .cseg
    .org 0
    rjmp RESET ;Reset Handler
    nop ;rjmp INT0 ;External Interrupt0
    nop ;rjmp INT1 ;External Interrupt1
    nop ;rjmp ICP1 ;Input capture interrupt 1
    nop ;rjmp OC1A ;Timer/Counter1 Compare Match A
    nop ;rjmp OVF1 ;Overflow1 Interrupt
    rjmp OVF0 ;Overflow0 Interrupt
    nop ;rjmp URXC0 ;USART0 RX Complete Interrupt
    nop ;rjmp UDRE0 ;USART0 Data Register Empty Interrupt
    nop ;rjmp UTXC0 ;USART0 TX Complete Interrupt
    nop ;rjmp ACI ;Analog Comparator Interrupt
    nop ;rjmp PCINT ;Pin Change Interrupt
    nop ;rjmp OC1B ;Timer/Counter1 Compare Match B
    nop ;rjmp OC0A ;Timer/Counter0 Compare Match A
    nop ;rjmp OC0B ;Timer/Counter0 Compare Match B
    nop ;rjmp USI_START ;USI start interrupt
    nop ;rjmp USI_OVF ;USI overflow interrupt
    nop ;rjmp ERDY ;EEPROM write complete
    nop ;rjmp WDT ;Watchdog Timer Interrupt
    ; for compatibility purpose

    ;======================================================================
    ; Инициализация

    RESET:
    ldi temp,low (RAMEND) ;Определение начала стека
    out SPL,temp

    ldi temp,0b0001111 ;Конфигурирование портов
    out DDRD,temp ;Порт D вход\выход
    ser temp
    out DDRB,temp ;Порт B выход
    out PIND,temp ;Команда для отладчика
    out PORTD,temp ;Включаем подтягивающие резисторы
    out PORTB,temp ;Выключаем матрицу

    ldi temp,15 ;Инициализация сторожевого таймера
    out WDTCR,temp
    rcall TNCT_0 ;Инициализация таймера ТС0

    ;======================================================================
    ; для проверки работы индикатора выводит на индикатор число 4321
    ; запятая во втором знакоместе
    ldi Temp,4
    sts Digit ,Temp ;загрузка начальных значений число 4321
    ldi Temp,3
    sts Digit+1,Temp
    ldi Temp,2
    sts Digit+2,Temp
    ldi Temp,1
    sts Digit+3,Temp

    ldi Temp,1
    sts Point,Temp ;загрузка номера разряда запятой 2-й разряд
    clr counter

    ;======================================================================
    ; Основной блок программы

    main:

    wdr ;»Бросим кость собаке»

    rjmp main ;Начинаем всё сначала

    ;=======================================================================
    ; Инициализация таймеров

    TNCT_0:
    cli ;запрещаем прерывания
    ldi temp,N_TC0_2500uc ; инициализируем таймер Т\С0 на Т=0,0025 с
    out TCNT0,temp
    ldi temp,0b00000000
    out TCCR0A,temp
    ldi temp,(1<<CS02)+(1<<CS00) ;прескайлер таймера 1024
    ;ldi temp,(1<<CS00) ;прескайлер таймера 1 для отладки
    out TCCR0B,temp
    ldi temp,(1<<TOIE0) ;прерывания Т\С0 по переполнению
    out TIMSK,temp
    sei
    reti

    ;========================================================================
    ; Обработчик прерываний

    OVF0:
    rcall TNCT_0 ;Инициализация таймера ТС0
    ldi ZL,Low(Digit) ;инициализация массива
    ldi ZH,High(Digit)
    clr temp2
    add ZL,counter ;выбираем нужный номер ячейки
    adc ZH,temp2
    ld digit_out,Z ;загружаем ячейку
    lds n_point,Point
    rcall Decoder_n_digit
    rcall Decoder
    out PORTD,n_digit
    out PORTB,digit_out
    inc counter ;увеличиваем номер разряда
    mov temp,counter
    andi temp,(amount_digit-1) ;отсекаем количество разрядов по маске
    mov counter,temp
    reti

    ;=========================================================================
    ; Подпрограммы
    Decoder:
    ;преобразует десятичную цифру разряда в 7-ми сегментный код индикатора

    clr temp
    ldi ZL,Low(DcMatrix*2) ;инициализация массива перекодировки
    ldi ZH,High(DcMatrix*2)
    clr Temp2 ;прибавление переменной
    add ZL,digit_out ;к 0-му адресу массива
    adc ZH,Temp2
    lpm ;загрузка значения 7-ми сегментного кода индикатора
    mov digit_out,r0
    clr temp
    cp n_point,temp ;проверяем номер разряда зяпятой если
    breq PC+6 ;если он равен 0 то выходим из подрограммы
    cp counter,n_point ;если он не равен нулю сравниваем с
    brne PC+4 ;номером разряда вкл в данный момент
    mov temp,digit_out
    andi temp,0b01111111 ;если равен зажигаем зяпятую иначе
    mov digit_out,temp
    ret ;выходим из подпрограммы

    DcMatrix:
    ;массив — таблица истинности декодера
    ; hgfedcba hgfedcba
    .db 0b11000000,0b11111001 ;0;1
    .db 0b10100100,0b10110000 ;2;3
    .db 0b10011001,0b10010010 ;4;5
    .db 0b10000010,0b11111000 ;6;7
    .db 0b10000000,0b10010000 ;8;9

    Decoder_n_digit:
    ldi ZL,Low(DnMatrix*2) ;инициализация массива
    ldi ZH,High(DnMatrix*2)
    clr Temp2 ;прибавление переменной
    add ZL,counter ;к 0-му адресу массива
    adc ZH,Temp2
    lpm ;загрузка значения
    mov n_digit,r0
    ret

    DnMatrix:
    ;массив — таблица истинности декодера разряда

    .db 0b11111110,0b11111101 ;0;1
    .db 0b11111011,0b11110111 ;2;3

  11. Flint говорит:

    Вопрос по .DSEG
    Если взять структуру исходных файлов, как, например, в прогах ADCsoftFilter, UARTundSoftADC, в каком месте нужно её указывать, или это все равно — компилятор сам разберётся?
    «var1: .BYTE 1″ А как задать 2-х байтную переменную в ОЗУ, var1: .BYTE так?
    И отсюда же следующий вопрос — как задать массив 2-х байтных чисел в ОЗУ. Может быть надо уазать var1: .BYTE 8 — это массив из 4-х двухбайтовых значений, правильно? Ну и соответственно, работать с ними, подразумевая, что каждый следующий элемент идёт через 2 байта.

    • DI HALT говорит:

      DSEG везде где не CSEG :) Я обычно перед CSEG ставлю. Сразу после инклюдников обычно.

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

      • Flint говорит:

        Собственно, я поставил в vectors.asm, но тоже получается перед .CSEG. Надо мне будет, наверное, завести отдельный фалик только с переменными ОЗУ и записать их все туда :) И подключить includе’ом в нужном месте

  12. Flint говорит:

    А как задать 2-х байтную переменную в ОЗУ, var1: .BYTE 2 — опечатался
    А запись var1: .BYTE 8 я имею ввиду рассматривать как «массив из 4-х двухбайтовых значений», хотя ассемблеру, возможно, мои мысли и неведомы и он подразумевает просто последовательность данных в ОЗУ

    • DI HALT говорит:

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

      • Flint говорит:

        Инициализировать-то потом не проблема. Главное правильно мне их задать. А «адреса придется вручную считать» это в смысле подразумевается 1-и 2-х байтовый элемент — это адрес 0-го + 2 ? Ну это тоже не очень большая проблема. Ведь в программе же я оперирую символьным имененм, и следующий элемент массива находится по вышеприведённому способу исходя из размера элемента массива.

  13. tarasadidas говорит:

    И опять вопрос по оператору (.BYTE) .
    Если мы выделили массив с меткой Variablе:
    как установить указатель допустим Z на метку массива

    ldi Zh,high(Variablе)
    ldi Zh,low(Variablе)
    а считывать массив
    st Z+,r16
    st Z+,r17
    и т.д.
    Или я ошибаюсь?

  14. tarasadidas говорит:

    DI HALT Спасибо ошибки замелил сам но отредактировать не смог не поддерживается у вас чтоли.
    Но самое главное суть понял а позже буду внимательней уж извини.

  15. tarasadidas говорит:

    И еще вопрос по .include .Вставлять я так понял этот оператор мона где угодно хоть посреди кода а помещать в прикрепляемый файл как куски того же кода так и подпрограммы (даже обработчики прерывания).
    И еще расширение у файла должно быть асм. или произвольное и помещать его нуна в папку текущего проекта?
    Я так понимаю что любой отлаженный кусок кода (инициализация портов) или подпрограмму просто вырезаем и сохраняем как отдельный файл с расширением асм. в папке проекта
    а в том месте пишем .include имя файла.asm или я не прав?

    • DI HALT говорит:

      Да. Расишерие может быть любым. Главное чтобы это был текстовый файл. Быть он может тоже где угодно, но длинные пути и пути с русскими буквами плохо отрабатываются.

  16. tarasadidas говорит:

    Проверил расширение только asm катит.Иначе студия матерится.

  17. Spok говорит:

    Я могу ошибаться, но в статье косяк: надо не .EESEG, а .ESEG.

  18. Flint говорит:

    Не пойму, проект на основе Atmega48, при компиляции запнулось повидимому на макросе OUTI с такой ошибкой: macro.asm(6): error: Operand 1 out of range: 0xc4 И указывает на вторую строчку макроса, т.е. OUT @0,R16. И еще куча подобных ошибок, где этот макрос встречается. Мой проект http://slil.ru/28665386

    • DI HALT говорит:

      Да, все верно. Открывай файл m48def.inc и смотри карту ио регистров. Те из них что помечены как memory mapped недоступны для команды OUT|IN которая используется в макросе OUTI. Загрузка/выгрузка в них идет как в ячейку памяти по командам STS/LDS

      т.е. меняем OUT на STS и все работает.
      Сделай еще один макрос OUTIm такого вида и юзай его для мемори маппед регистров.

  19. ZhekSooN говорит:

    Опа, никто не упомянул о таких дьявольских штуках, как .if\.endif, .ifdef\.endif :)
    С их помощью можно создать почти свой язык программирования :)

    • DI HALT говорит:

      Точно. Совсем про них забыл. Надо добавить. Кстати, а AVRassebler1 их поддерживает? В доке про них нет ни слова.

    • auara говорит:

      Общие макросы переношу из проекта в проект и подключаю через:
      .include «MACROS.MAC»

      (кстати, кто-то жаловался что только расширение «.asm»)

      Вот примерчик, работающий на разных AVR8

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      
      ;***************************************************************************
      .macro	mEORBi		;XOR Bit in register I/O
      .IF @0<64
      	IN	rM,@0
      	EOR	rM,@1
      	OUT	@0,rM
      .ELSE
      	LDS	rM,@0
      	EOR	rM,@1
      	STS	@0,rM
      .ENDIF
      .endmacro
      ;;--------------------	mEORBi	regIO,reg
      ;;--------- Ex: ------	mEORBi	TIFR,R17
  20. ZhekSooN говорит:

    Насчет первой версии не знаю, но во второй всё охрененно пашет. Я рад: создал скрипты инициализации таймеров, USART, ADC и других нудных вещей — и не паришся!
    Это как Сишная вставка посреди ассемблерного поля :)

  21. A_ndrej говорит:

    Добрый день.
    Подскажите пожалуЙста, можно ли в макросе, в качестве параметра»@» ставить ссылку на подпрограму?
    Например:
    ,MACRO CALC_1
    ldi @1, @2
    cp r16, @2
    BREQ @3

    И тогда при вызове макроса написать:
    CALC_1 r17,r18,METKA
    где «METKA» будет являтся той ссылкой, на которую с макроса пойдет контроллер.

  22. A_ndrej говорит:

    Большое спасибо.

  23. dima_m говорит:

    Сейчас пробовал макрос забабахать. Супер, все работает. Реально удобно. Уже начал использовать в деле. Только вот хочется узнать, откуда у DIHALTA слева в редакторе появился столбец с цифрами, то есть нумерация строк. У меня в AVR STUDIO нет такой фишки. Не подскажете кто, как такую себе сделать?

    • DI HALT говорит:

      У меня тоже нет, а с чего ты взял, что у меня в АВР студио есть нумерация?

      • dima_m говорит:

        Ну вот, когда ты вверху показываешь пример кода. У тебя в редакторе слева есть нумерация строк. Да и сам редактор привлекательный. Попривлекательнее чем в AVR. Просто думал ты какой нибудь плагин доставил к проге. Значит это не AVR STUDIO. А что же это за редактор?

  24. bdp говорит:

    DI HALT в придложении
    >В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов
    >фразы “привет лунатикам”, а дальше 000A, 000B, 000F.
    >Последнии числа, хоть сами и невелики, но занимают по два байта каждое, так как .dw.
    несостыковка с примером идущим сверху в котором нет описи «dw».

    Это при условии что я ничего не пропустил :)

  25. auara говорит:

    Еще упущена «.SET» (про «.IF», «.IFDEF», «.IFNDEF» уже вспоминалось)

    Вот кусок из программы:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    |.set uD=$00
    |.set uB=$00
    |;————————
    |.IF cInpMax >= $0C
    | .IFNDEF cInp1_Port or cInp1_Pin
    | .ERROR “Not defined cInp1_Port or cInp1_Pin”
    | .ENDIF
    | .set U=$FF-(1<<cInp1_Pin)
    | .if cInp1_Port == PortD
    | .db uD&U, U
    | .db uB, $FF
    | .else
    | .db uD, $FF
    | .db uB&U, U
    | .endif
    |.ENDIF
    |
    • auara говорит:

      ну Блин!
      Уже и вертикальную черточку поставил, чтоб не сплюскивало, так автоматика сайта пробелы сократила. Компьютер и так прохавает, а я людям для наглядности форматировал. Для восприятия лучше…
      - — — — — — — — — — — -
      Может программка есть какая?
      Написал в ней, отформатировал, сюда вставил и все красиво.
      А если нету — то надо на сайте что-то менять.
      Ну напрягает… тексты программ в комментариях ко всем статьям — просто каша.
      я то разберусь, но начинающие могут упустить(не понять) много полезного.

      • auara говорит:

        Глянул на местный форум — ситуация та же… код-лист == каша.
        Даааа уж… Видимо не все так просто.
        Удивительно! Обсуждения концепций OS и каменный век — рядом!

        • DI HALT говорит:

          Я не хтмл программист. А готовый плагин который бы мог это делать я не нашел.

          • auara говорит:

            Спасибо, вижу ты облагородил сообщение «апреля 24, 2010 at 4:26″
            Еще и в «24 Апр 2010 4:45″ желательно убрать передующие «|» и сдвиги добавить. Плиз…

          • DI HALT говорит:

            лень

          • auara говорит:

            Я думал ты сидишь на готовом движке сайта. А если все сам…
            Ну может кто поможет/подскажет DI HALT-у доработать/переработать обработку сообщений на сайте, а то и сайт перемастырить.
            Ему над уроками надо мозговать, а он тратит время на редактирование наших текстов.

          • DI HALT говорит:

            На готовом и сижу. Потому и не могу взять и что то там подкрутить если этого нет в настройках.

  26. auara говорит:

    После установки AvrStudio4, запустить файлик помощи:
    AVRTools\Help\AVRASM.chm
    Там в закладке «Содержание» выбираем (раскрываем)
    -> «AVR Assembler» -> «User’s Guide» -> «Directives»
    И вот здесь, в разделе «Assembler directives», внимательно рассматриваем/изучаем директивы и примерчики (как раз по нашему учебному курсу).

  27. dima_m говорит:

    DI HALT привет. А что означает выражение: макрос — лучше оформить в виде процедуры или функции? У тебя на сайте это где-то объясняется? Я бы почитал.

  28. skywalker говорит:

    Constant: .db 10 ; или 0хAh в шестнадцатеричном коде
    Message: .db «Привет лунатикам»
    Words: .dw 10, 11, 12

    В итоге, во флеше вначале будет лежать число 0А, затем побайтно будут хекскоды символов фразы “привет лунатикам”, а дальше 000A, 000B, 000F.

    А разве последняя строчка не равна 000A,000B,000C?

  29. dima_m говорит:

    1. Получается каждая буква фразы .db «Привет лунатикам» займет один байт во флеш памяти по порядку?
    2. И соответственно вместо каждой буквы будет вбит ее ASII символ?
    3. Как бы это применить? Можно ли насоздавать таких сообщений которые нужно вывести на LCD дисплей, потом как нибудь вызывать эти метки?

    • DI HALT говорит:

      1. Да
      2. Да
      3. Так и применяют, вписывая текстовые строки во флеш. О том как вызываются текстовые метки из памяти программ написано далее в курсе. Где то про работу с памятью через команды LPM

      • dima_m говорит:

        Ты имееш в виду это?

        LDI ZL,low(data*2) ; заносим младший байт адреса, в регистровую пару Z
        LDI ZH,high(data*2) ; заносим старший байт адреса, в регистровую пару Z
        ; умножение на два тут из-за того, что адрес указан в
        ; в двубайтных словах, а нам надо в байтах.
        ; Поэтому и умножаем на два
        ; После загрузки адреса можно загружать число из памяти

        LPM R16, Z ; в регистре R16 после этой команды будет число 12,
        ; взятое из памяти программ.

        ; где то в конце программы, но в сегменте .CSEG
        data: .db 12,34,45,23

  30. henvert говорит:

    Подскажите пожалуйста где можно найти полное описание макроассемблера для AVR.
    наткнулся на такой кусочек кода

    ; тактовая частота МГц
    #define XCLK 9.6
    .equ CN2MKS = INT((2*XCLK-7)/3) ; константа 2мкс
    .equ CN5MKS = INT((5*XCLK-9)/3) ; константа 5мкс
    .equ CN10MKS = INT((10*XCLK-9)/3) ; константа 10мкс
    .equ CN50MKS = INT((50*XCLK-9)/3) ; константа 50мкс

    А что такое INT() не понятно((

  31. salpingot говорит:

    Я так и не разобрался с макросом, (хотя уже успел полистать и книжку revich-yuriy, да и в инете покапашился), но все равно пока нет полного списка операторов и их описания для ASM&AVR. Единственгное что понял точно, что макрос может иметь до 10 параметров, к которым в его теле обращаются через @0,@1,@2 и т.д. до @9, и что макрос это повторяющийся кусок кода который при трансляции просто тупо заменяет метки в тексте программы на «тело» макроса, т.е. экономии в размере самой программы нет. Применение макросредств целесообразно когда критично время выполнениия программы, а если нужна экономия памяти, тогда нужно использовать процедуру.

    Взял вот отсюда: http://www.asm-faq.ru/direktivy-i-operatory-assemblera/69-makrooperatory.html

    Макрооператоры
    Макрооператор & (амперсанд) нужен для того, чтобы параметр, переданный в качестве операнда макроопределению или блоку повторений, заменялся значением до обработки строки ассемблером. Так, например, следующий макрос выполнит команду PUSH EAX, если его вызвать как PUSHREG A:

    pushreg macro letter
    push e&letter&x
    endm

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

    irp number,
    msg&number db ?
    endm

    Макрооператор (угловые скобки) действует так, что весь текст, заключенный в эти скобки, рассматривается как текстовая строка, даже если он содержит пробелы или другие разделители. Как мы уже видели, этот макрооператор используется при передаче текстовых строк в качестве параметров для макросов. Другое частое применение угловых скобок — передача списка параметров вложенному макроопределению или блоку повторений.

    Макрооператор ! (восклицательный знак) используется аналогично угловым скобкам, но действует только на один следующий символ, так что, если этот символ — запятая или угловая скобка, он все равно будет передан макросу как часть параметра.

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

    Макрооператор ;; (две точки с запятой) — начало макрокомментария. В отличие от обычных комментариев текст макрокомментария не попадает в листинг и в текст программы при подстановке макроса. Это сэкономит память при ассемблировании программы с большим количеством макроопределений.

    ВОПРОС: ЧТО ТАКОЕ ПАРАМЕТРЫ И КАК ЭТИМ ПОЛЬЗОВАТЬСЯ С ПРИМЕРАМИ ПОПРОЩЕ ТАК ЧТО БЫ МОЖНО БЫЛО БЫ УЛОВИТЬ СУТЬ, И СОСТАВТЕ ПОЖАЛУЙСТА СПИСОК МАКРООПЕРАТОРОВ И ОПИСАНИЕ К НИМ С ПРИМЕРАМИ.
    с уважением Salpingot.

    • salpingot говорит:

      Я в курсе что это для х386 tasm masm и т.д.

      • DI HALT говорит:

        Прикол в том, что AVRASM не умеет и 5% того, что умел тасм. Так что тут почти все что он умеет.

        Примеры попроще я привести не могу. Куда уже проще?

        • salpingot говорит:

          Уважаемый Ди Халт, а можно описать каждую директиву предпроцессора, (т.е. для чего она нужна, где не верно внесите исправления пожалуйста, некоторые без перевода не знал как перевести):

          Preprocessor directives, КАК Я ПОНЯЛ ИХ ВСЕГО 14.
          1. #define
          2. #elif
          3. #else, ИНАЧЕ, ТОГДА, лучше всего смотрится с комбинацией if (если, тогда/иначе)
          4. #endif
          5. #error, при трансляции вывод ОШИБКИ,
          6. #if , условие ЕСЛИ,
          7. #ifdef ЕСЛИ_СИМВОЛИЧЕСКОЕ ИМЯ ???
          8. #ifndef ОТМЕНИТЬ ЕСЛИ_СИМВОЛИЧЕСКОЕ ИМЯ ???
          9. #include, ВСТАВКА ФАЙЛА с другими макросами, подпрограммами (процедурами) и т.п.
          10. #message в ходе трансляции вывод СОБЩЕНИЯ,
          11. #pragma
          12. #undef ОТМЕНИТЬ СИМВОЛИЧЕСКОЕ ИМЯ
          13. #warning, ПРЕДУПРЕЖДЕНИЕ, типа смотри сюды и будь внимателен.
          14. # (empty directive) ЗНАЧОК ПУСТОЙ ДИРЕКТИВЫ

          Я правильно понимаю, что есть только ТРИ макрооператора: ‘@’ и “.” ‘#’ при составлении макросов???
          Идеальный вариант с примерам на каждый макрооператор (можно один большой).
          ДА И ДЛЯ НОВИЧКОВ ТАКИХ КАК Я, МОЖЕТ ПРИГОДИТЬСЯ Справка по Ассемблеру для Atmel AVR
          1. http://dfe3300.karelia.ru/koi/posob/avrlab/avrasm-rus.htm
          2. http://mymcu.ru/Articles/Atmel11.htm
          это понадобиться для того что бы было понятно дальше о чем пишет Ди Халт.

          • salpingot говорит:

            а вот пример и попроще как использовать макрос для включения ноги мк D7 на пин боард, т.е. в коде в том месте где нужно, можно просто написать имя макроса (у меня включение ноги D7 называется noga_mk_D7), как и говорил выше Ди Халт, с помошью макроязыка можно существенно оптимизировать программирование на асемблере.

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            
            begin:		nop;
            			nop;
            .macro    	noga_mk_D7;
            			ldi		r17,0b11111111;
            			out		DDRD,r17;
            			ldi		r16,0b10000000;
            			out		PORTD,r16;
            .endm
            			nop;
            			nop;
            			noga_mk_D7;
            			nop;
            			nop;
            zamk_cikl:			
            			nop;
            			nop;
            			jmp		zamk_cikl;
          • DI HALT говорит:

            Пример крайне неудачный. Т.к. планируется включить вывод 7, а по факту меняем содержимое всего порта и воздействуем на выводы 0..6

            Если уж делать то так:

            .macro noga_mk_D7
            SBI DDRD,7
            SBI PORTD,7
            .endm

          • AlexRom говорит:

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

          • DI HALT говорит:

            В смысле через метку? В подпрограмму вынести? Не проще. ПРидется делать вызовы CALL и RET, что сожрет всю экономию.

          • AlexRom говорит:

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

            rjmp noga_mk_D7;
            noga_mk_D7:
            SBI DDRD,7
            SBI PORTD,7
            Может я и ошибаюсь.
            P.S. Огромное спасибо DI HALT, помощь огромная благодаря этому сайту!

          • DI HALT говорит:

            Ну да, сделаешь ты джамп, а как вернешься обратно?

          • AlexRom говорит:

            salpingot благодарю за ссылки ;)

  32. maxgrind говорит:

    а ячейки озу можно как нибудь задефайнить?

  33. salpingot говорит:

    ; В свое время не смог понять смысл параметра в макросе,
    ; и тут меня осенило решил поделиться с теми до кого не дошло
    ; что такое параметр. Параметр — это переменная (X,Y и т.п.) в уравнении
    ; макроса, они будут заменены на значения РОН, (0-255)
    ; или выражения (.equ или .def) или и т.п.
    ; нумерация начинается с нуля разделяются запятой 0@,@1,@2,@3…@9,
    ; итого 10 параметров в теле одного макроса. Т.е. с помощью
    ; макросов можно писать своеобразные уравнения/формулы для
    ; значений в программе.
    .MACRO stek;
    ldi @1,low(@0);
    ldi @2,high(@0);
    out SPL,@1;
    out SPH,@2;
    .ENDM;
    ; применение макроса с параметрами для иницилизации стека одной строчкой,
    stek (RAMEND),r20,r21; где:
    ; ^ ^ ^ ^ (попытался замутить стрелочки
    ; имя макроса | | | не знаю на сколько получилось)
    ; где параметр @0 , @1, @2, и т.д. можно до @9 разделитель запятая.
    ; и еще одна фишка если скампилировать код (ctrl + F7), и навести после
    ; в моем примере на (RAMEND) то можно будет увидить чему равнялось
    ; значение .equ или .def можете проверить в файле m16def.inc,
    ; значение а там .equ RAMEND =$45F, удобно получается очень, не держать
    ; все переменные в голове.
    ; ДА И ДЛЯ НОВИЧКОВ ТАКИХ КАК Я, повторю ссылки еще раз,
    ; МОЖЕТ ПРИГОДИТЬСЯ Справка по Ассемблеру для Atmel AVR
    ; 1. http://dfe3300.karelia.ru/koi/posob/avrlab/avrasm-rus.htm
    ; 2. http://mymcu.ru/Articles/Atmel11.htm
    ; как смог объяснил, если что пишите в личку, и обязательно на форум, что бы
    ; другие товарищи смогли нагуглить и ознакомиться.

  34. salpingot говорит:

    А вот и дерективы препроцессора ссылочка для новичков http://azbukavb.narod.ru/cdoc/pdirectives.html

  35. sam505 говорит:

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

  36. salpingot говорит:

    Что значит “ассемблер для AVR для занятых”? типа времени мало а хочу много? есть краткие курсы см. раздел книги на этом сайте справа есть указатель http://easyelectronics.ru/category/knigi скачай в телефон и читай. а если совсем коротко то можно обойтись и двумя командами, sbi ddrX, ? (где ? номер ножки) sbi portX,? (где ? номер ножки) вкл, сbi portX,? (где ? номер ножки) диодик выкл. (впервое время всегда хочется экшена, хотя бы проверить работает ли хоть что нибудь) Все равно нужно время, может уточнишь что значит занятых???

  37. sam505 говорит:

    Простите, что не уточнил. Время выделено и почитать можно и с эрана и с твердой копии.
    От уважаемого сообщества мне нужен необходимый и достаточный перечень литературы по теме. Так чтобы лаконично, но твердо схватить ассемблер за хвост. Можно взять не Ту книжку и… :)
    З.Ы. Когда разменяешь 5й десяток, то могзи скрипеть начинают от накопленной инфы… :).

  38. d-lun говорит:

    Странно, у меня компилятор на .undef ругается.

    • DI HALT говорит:

      Вот чо гласит мануал:

      This directive is only available with AVRASM2.

      ‘The UNDEF directive is used to undefine a symbol previously defined with the DEF directive. This provides a way to obtain a simple scoping of register definitions, to avoid warnings about register reuse.

      Syntax:
      .UNDEF symbol

      Example:
      .DEF var1 = R16
      ldi var1, 0×20
      … ; do something more with var1
      .UNDEF var1

      .DEF var2 = R16 ; R16 can now be reused without warning.

      Т.е. надо в качестве компилятора указать avrasm2

  39. gks5 говорит:

    Опечатка:
    Указывать размеры перменных нужно для того

  40. tyuseman говорит:

    Читал статью «Программы и прерывания». Дошёл до раздела «Подпрограммы vs Макросы». Решил попробовать написать макрос, аналогичный подпрограмме Wait (цикл DELAY раз). Получилось:

    .MACRO MWAIT
    M1: LDI R17, @0
    DEC R17
    NOP
    BRNE M1
    .ENDM

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

    P.S. Почему-то под той статьёй комментарий не смог оставить. Пишет «необходимо войти».

    • DI HALT говорит:

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

      В макросах нельзя использовать метки, т.к. если макрос используется дважды. то метка повторится, а это моветон. Но можно использовать переходы со смещением типо BRNE PC-1 или PC+1 где PC текущая строчка, а +X или -X смещение в словах относительно нее. Но учти что не все команды занимают размер одно слово. Бывают и в два слова. Тут лучше под отладчиком прогонять такой кусочек прежде чем его в макрос пихать.

      • tyuseman говорит:

        Спасибо! Понял — всё руками, попробую. Только сразу скажу — не то, что надолго втупляет — а как будто бы навсегда :). То есть ставлю брейк на следующую после макроса команду, F5, ну и всё — висим.

        • tyuseman говорит:

          Ой, я дурак! Метку на LDI, а не на DEC поставил. Но всё равно было полезно — узнал, что всё же нельзя метки ставить в макрос на случай повторения.

          • DI HALT говорит:

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

      • meps говорит:

        А что — совсем нет локальных меток в макросах? По-моему в masm под x86 локальные метки есть. Я хочу использовать вложенные макросы и не хотелось бы каждый раз вручную вычислять длину перехода.

  41. lixlex говорит:

    а есть директивы как в ассемблере под mcs51 типа .org чтобы распологать команды в определенных местах памяти?

  42. oleg22ov говорит:

    здравствуйте. Di halt скажи че не так я написал, студия ругается.
    ругается на макрос pop_stec

    .macro push_stec
    in r00,sreg
    push r00
    .endmacro

    .macro pop_stec
    pop r00
    out r00,sreg
    .endmacro

    ;==========================================êîíåö ìàêðîñàì=======================

    ;———————————————îáðàáîò÷èêè ïðåðûâàíèé————-

    rx_ok: push_stec
    in r16,udr

    pop_stec
    reti

  43. tiamat-666 говорит:

    Здраствуйте. Вот прачьол статью все коментарии, и так не могу понять что такое параметр макроса «@0, @1,…@9″. Если можна поясните болие детально, ато зациклиса ни них!!! :-(
    Интересуют такие свойства как:
    1. Что такое етот параметр «@» детальная инфа ?
    2. Как присвоить ему дание? тоесть,обявление макроса(возьмем пожалуй товой пример DI HALT):
    .MACRO SUBI16
    subi @1,low(@0) ———вичитать с параметра 1 параметр 0 (но где и каким образом мне задать ети значения, дание или константи, чтоб копилятор занал что вичитать)
    sbci @2,high(@0)
    .ENDM

    Зарание большое спасибо

Добавить комментарий