Ассемблер (ASM) для AVR в вопросах и ответах

Обсуждаем контроллеры компании Atmel.
Ответить
Вымогатель припоя
Аватара пользователя
Сообщения: 606
Зарегистрирован: Вт июн 25, 2013 18:45:07

Сообщение Andrew Martin »

Дело в том, что CPC практически не используется без CP. Если бы флаг Z у CPC изменялся всегда, то нельзя бы было эту инструкцию использовать по прямому назначению для сравнения чисел большой разрядности. Например, сравните 0x5000 0x5050. Сначала СР сравним младшие байты, Z=0, а потом СРС сравним старшие. Если бы CPC выдавала зеро флаг только основываясь на текущем результате, то мы бы имели Z=1, что означало бы равенство неравных 16-битных чисел, а так Z=0*Z = 0*1 = 0, и это правильно.
По-моему всё очевидно и давно разжевано.
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 650
Зарегистрирован: Пн июн 18, 2012 12:01:04
Откуда: Челябинская область, Копейск

Сообщение zero648 »

Z_h_e писал(а):Так нет в AVR 16 битных регистров, ибо шина 8ми битная.

Попробуйте написать код сравнения чисел (т.е. из первого вычесть второе) , пускай будут они равны 0x0100 и 0x0001 и сделать переход по неравенству. Сколько у Вас будет команд, если флаг Z меняется "классически" не только для CP, но и CPC?
Понятно, что шина 8 битная, но в примере сравнивается 16 битное число, ну это в принципе не важно, пусть будет сколько угодно команд сравнения n-битного числа, решающая команда все равно последняя, последняя это та, после которой вы собираетесь что-то сделать по условию, именно последняя команда устанавливает результирующие флаги. Можно проверить сбросив или установив флаг нуля, т.к. про него разговор, перед последней командой сравнения, результат на выходе будет одинаковый.
Для примера: в первом и втором случае на выходе ничего не изменится, в данном случае нельзя трогать только флаг переноса.

Код: Выделить всё

; Compare r3:r2 with r1:r0
cp r2,r0 ; Compare low byte
clz
cpc r3,r1 ; Compare high byte
brne noteq ; Branch if not equal

Код: Выделить всё

; Compare r3:r2 with r1:r0
cp r2,r0 ; Compare low byte
sez
cpc r3,r1 ; Compare high byte
brne noteq ; Branch if not equal
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Я Вам же не зря предложил сравнить два числа. 0x0100 и 0x0001.

Предположим команды СP и CPC устанавливают флаг одинаково и всегда.
Напишу код сравнения, но без регистров, а сразу значение из них.

Код: Выделить всё

CP 0x00,0x01
CPC 0x01,0x00
breq RAVNO
Результат первой команды: Z=0,C=1
Результат второй, которая почему то решила устанавливать флаги так же как первая: Z=1, C=0
Результат третей, это прыжок , как будто бы 16ти битные числа равны, а они не равны.

Вот потому команда CPC умеет только сбрасывать Z если не равно, и оставлять его предыдущим значением если равно. Это и позволяет сравнивать большие числа малым числом команд.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Друг Кота
Аватара пользователя
Сообщения: 4906
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Z_h_e писал(а):Если бы флаг Z у команды CPC изменялся также как и у CP,т.е. всегда.
То весь Ваш код был бы не рабочим.
Ну почему же? Как бы ни менялся этот флаг у этих команд, он на их результаты никак не влияет. CPC при выполнении учитывает только флаг С, ну а то, что она ещё и на Z влияет, не важно, т.к. его для вычислений не использует.

P.S. Тут лучше, конечно, BRCC/BRCS использовать вместо BREQ/BRNE, вообще игнорируя Z, который при сравнении многобайтовых слов, в принципе, не нужен.
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

:kill: Я не знаю что ответить.
Только повторить написанное.

Вот так устанавливает флаг Z команда CPC
Z= R7 ¯ • R6 ¯ • R5 ¯ • R4 ¯• R3 ¯ • R2 ¯ • R1 ¯ • R0 ¯ • Z
Z_h_e писал(а):Смысл данного выражения в том, что команды типа SBC, CPC сбрасывают Z, если результат не равен нулю, а если результат нулевой, то во флаг Z будет записано его предыдущее состояние.
Если бы флаг Z устанавливался всегда командой CPC, без учета его предыдущего значения флага Z, то связка

Код: Выделить всё

СP
CPC 
CPC
...
не работала бы.
Я даже привел пример с конкретным числом.
P.S. Тут лучше, конечно, BRCC/BRCS использовать вместо BREQ/BRNE, вообще игнорируя Z, который при сравнении многобайтовых слов, в принципе, не нужен.
Как на равенство проверять с помощью флага С?
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 650
Зарегистрирован: Пн июн 18, 2012 12:01:04
Откуда: Челябинская область, Копейск

Сообщение zero648 »

Вот так уже логично будет.
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

А до этого что было нелогично?
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Друг Кота
Аватара пользователя
Сообщения: 4906
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Z_h_e писал(а): Как на равенство проверять с помощью флага С?
Да, тут я с BREQ/BRNE, пожалуй, несколько ошибся. Одного флага будет недостаточно, например, для чисел вроде 0x5678 и 0x5531 (младшие байты не равны, старшие отличаются на 1) будет ошибочно воспринято равенство. 0x78..0x31 установят C, а 0x56..0x55+1 ошибочно его снимут.
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Сб сен 17, 2016 18:56:30

Сообщение dimmer »

dimmer писал(а): В Атмелевском файле AVR202.asm представлена идиома "Negate a 16-bit Register Variable":

Код: Выделить всё

	com	ng1l
	com	ng1h
	subi	ng1l,low(-1)
	sbci	ng1h,high(-1)
на самом деле можно сделать короче:

Код: Выделить всё

	neg	ng1h
	neg	ng1l
	sbci	ng1h,0
Выше в этой ветке я приводил ещё пример использования особенности флага С.
Друг Кота
Аватара пользователя
Сообщения: 15793
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Как проверять на равенство... да много байтев...
адрес массива в указатель (X,Y,Z)
какой-нить счетчик количества байт cntb...
ну и покатились...

Код: Выделить всё

loop_cp:
       ld r16,x+
       ld r17,y+
       spse r16,r17
         rjmp error
       dec cntb
       brne loop_cp
     return ; or rjmp adr
error:
    обрабатываем ошибку
как-то и без срс и размер массива побольше...
примерно таким же манером и по регистровому файлу пройтись можно

Код: Выделить всё

   cpse r16,r17
   rjmp error
   cpse r14,r15
   rjmp error
 нормальное завершение...
одначе собственно по случаю размещения данных в регистровом файле конструкция с срс более оптимальна...
:roll:

dimmer
"0 - число" не одно и то же, что "число - 0"...
ежли уж "сокращать"

Код: Выделить всё

   com   ng1l
   com   ng1h
   subi   ng1l,low(-1)
   sbci   ng1h,high(-1)
так используя то, что система команд АВР для двухбайтовых значений надурняк предоставляет

Код: Выделить всё

  com   ng1l
   com   ng1h
   adiw ng1l,1
разместив ng1h:ng1l где-нибудь в парах R25:R24, R27:R26, R29:R28 или R31:R30.
НО... поскольку последние три индекс-указатели, то для двухбайтовых и прочих фоуксов весьма напрашивается R25:R24 с выделением этой пары с отметкой "псевдобазовый регистр"...
:beer:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Сб сен 17, 2016 18:56:30

Сообщение dimmer »

Мой вариант работает с любой парой регистров старшей половины. Не вижу смысла делать себе тесно. А что такое "псевдобазовый регистр"?
akl
Друг Кота
Сообщения: 4473
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

BOB51 писал(а):Как проверять на равенство... да много байтев...
адрес массива в указатель (X,Y,Z)
какой-нить счетчик количества байт cntb...
ну и покатились...

Код: Выделить всё

loop_cp:
       ld r16,x+
       ld r17,y+
       CPSE R16,R17         ;имелась в виду spse r16,r17
         rjmp error
       dec cntb
       brne loop_cp
     return ; or rjmp adr
error:
    обрабатываем ошибку
как-то и без срс и размер массива побольше...
Команда CPSE сравнивает регистры, НЕ меняя флаги. Бывает полезно. По мне, лучше определить конец массива, по которому определять окончание процедуры. Тогда и счетчик cntb не нужен.

Код: Выделить всё

loop_cp:
       ld r6,x+
       ld r7,y+
       CP R6,R7
        BRNE  ERROR         ;здесь ставится любой(ые) BRxx 
 CPI YL,LOW(BUFER_END)
 BRLO loop_cp
;       dec cntb
;       brne loop_cp
     return ; or rjmp adr
error:
    обрабатываем ошибку
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Это в случае когда массив не пересекает переход старшего байта через 0, а это в общем случае неверно. Тем более для ассемблерной вставки, когда массив ей предоставляется языком высокого уровня и неизвестно заранее куда он его распределил.
А в таком случае придется проверять оба байта, то есть нет выигрыша ни по скорости, ни по ОЗУ. Выигрыш будет либо при ручном распределении памяти, либо если размер массива больше 256 байт, либо на младших моделях, где меньше 256 байт ОЗУ.

Код: Выделить всё

ldi temp, high(BUF_END)
cpi YL, low(BUF_END)
cpc YH,temp
brne loop_cp
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

COKPOWEHEU писал(а):Это в случае когда массив не пересекает переход старшего байта через 0, а это в общем случае неверно.
Если Вы про флаг C, то в данном циклическом алгоритме он и не нужен. Достаточно определить любую пару неравных регистров (или ее не найти), чтобы понять равные ли области памяти или нет. Так что алгоритм с командой CPSE в цикле рабочий.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Нет, я про определение конца массива.
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Можно еще глупость спрошу?
просто, когда я задаю здесь вопросы, то их надо написать, а когда пишешь, то их приходится понятно формулировать, а когда понятно формулируешь, часто ответ сам находится. . :)

Короче, можно ли в АВР загрузить в ОЗУ некоторый массив данных (довольно много, но определенный) из ФЛЕШа, а потом его тупо с какого-либо адреса начать читать и с легкой обработкой выдавать ПОБИТНО (последовательно) в порт? Или вообще без обработки отдавать ПОБИТНО в порт? Ну, например, пошли нули, то и вывод порта в нуле, пошли единицы - переключился в единицу...
понимаю, что это не СТМ32, у которого всякие DMI или как их там...
Станислав
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

СКАЗОЧНИК писал(а):Короче, можно ли в АВР загрузить в ОЗУ некоторый массив данных (довольно много, но определенный) из ФЛЕШа, а потом его тупо с какого-либо адреса начать читать и с легкой обработкой выдавать ПОБИТНО (последовательно) в порт? Или вообще без обработки отдавать ПОБИТНО в порт?
Конечно можно. Вот моя подпрограмма для печати строки, находящейся во флеш. Вам нужно делать также, только полученные данные не печатать, а помещать в ОЗУ.
Спойлер

Код: Выделить всё

;Подпрограмма вывода строки, хранящейся в программной памяти
;В регистровой паре Z начало строки, в регистре R19 адрес первой позиции вывода в строке дисплея	
PRNSTR:	push R18			;
	lsl ZL			;Умножить адрес начала строки на 2, так как доступ нужно
	rol ZH			;осуществлять побайтно
PRNS1:	lpm R18,Z+		;Занести в R18 байт из памяти программ, содержащий код выводимого символа
	cpi R18,$0		;Сравнение с нулем - кодом конца строки
	brne PRNS2		;Если принят не нуль, то переход на метку PRNS1
	pop R18
	ret			;Строка напечатана, выход из подпрограммы
PRNS2:	rcall PRNSYM		;Печать символа, хранящегося в R18, в позицию, адрес которой в R19
	inc R19			;Увеличение адреса для вывода следующего символа
	rjmp PRNS1		;Возврат на метку PRNSTR
А можно обойтись и без создания буфера в ОЗУ, если модификация данных не требуется. Сразу из флеш брать и отдавать
СКАЗОЧНИК писал(а):Ну, например, пошли нули, то и вывод порта в нуле, пошли единицы - переключился в единицу...
Написать подпрограмму, выдвигающую байт побитно в порт (на один из выводов порта) элементарно
Только надо протокол продумать - нужны ли старт-стоповые биты и т.д.
Друг Кота
Аватара пользователя
Сообщения: 15793
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Ежли сразу из флешки - скорость значительно ниже максимально возможной.
Это ежли только при внешнем параллельном ОЗУ (флешка с параллельным доступом) - тогда еще вписаться в максимум скорости можно.
СКАЗОЧНИК
Речь как я понял о тех же WS2812B?
я ж вроде уже ссыль на исходник того алгоритма давал...
:wink:
Там побитовая передача ШИМ 1/3-2/3 причем желательно без накопления "лишнего времени".
И никаких стартовых/стоповых - гонится массив по три байта на каждый светик без каких-либо разделителей и/или разрывов передачи.
Весьма тоскная задачка...
8)
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Да, про них...
Вашу реализацию уже изучал. Для меня замудрено.
Смысл ихнего протокола понимаю и скорости тоже. Сейчас попробую реализовать сам, посчитать все задержки, такты, циклы... А потом уже дальше с вопросами приду. :)
Станислав
Друг Кота
Аватара пользователя
Сообщения: 15793
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Не надо ничего там менять - программно более оптимальный вариант не получится - скорость там на пределе возможного (разве что только частоту еще до 20МГц поднять - рассчет велся/проверялся под 16МГц для внутреннего RC генератора) там в одном из последних слотов и так все такты выбраны.
Можно как в "типовых решениях" грузить рутину на SPI или USART -но там предварительное дробление для каждого байта и выбор лапок ограничен аппаратными "традициями".
:beer:
Ответить

Вернуться в «AVR»