Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Работа с опреативной памятью в AVR ASM
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
HoboTech
Здравствуйте, так получилось, что на C/С++ для АВРов начал писать раньше, чем на АСМе. Теперь не совсем понятно как корректно работать с переменными в SRAM.
Если на С задачу копирования адреса я решал так:
Код
unsigned char *ptr1, *ptr2;

ptr2 = ptr1;


и копирование значения (как вариант):
Код
unsigned char *ptr1, *ptr2;

*ptr2 = *ptr1;


то как такие задачи решаются на АСМе?

если написан следующий код:
Код
Tmp1:  .byte 1
Tmp2:  .byte 1

.....

lds r16,SPDR ;для примера взят регистр SPI
st Z+,r16

lds Tmp1,ZH
lds Tmp2,ZL


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

Надеюсь корректно описал вопрос.
ILYAUL
Код
lds Tmp1,ZH
lds Tmp2,ZL

Вы считывете в Tmp1- значение расположенное по адресу находящегося ZH и соответсвенно в Tmp2 в ZL т.е не понятно откуда

Скопировать значение mov TMP1,TMP2
Скопировать адрес MOVW X,Z
HoboTech
Цитата(ILYAUL @ Jan 18 2011, 12:38) *
Вы считывете в Tmp1- значение расположенное по адресу находящегося ZH и соответсвенно в Tmp2 в ZL т.е не понятно откуда

предполагается, что ранее в программе в Z что-то заносилось

Цитата(ILYAUL @ Jan 18 2011, 12:38) *
Скопировать значение mov TMP1,TMP2
Скопировать адрес MOVW X,Z

спасибо, буду пробовать

Вообще, наверно, проще будет описать задачу:
есть буффер из 50 элементов и в него заносятся последовательно байты; есть две байтовых переменных, в которых должна хранится high и low часть адреса следующего элемента, который будет записан (по аналогии с индексом массива в С/С++). В обработчике прерывания я заношу в буффер байт, инкрементирую регистр Z и нужно этот новый адрес записать для того, чтобы в следующем прерывании уже записывать новый элемент

реализация на С для IAR простенького кольцевого буффера:
Код
#include <ioavr.h>
#define EVER;;
#define BUFF_SIZE 50

typedef unsigned char BYTE;

BYTE Buff[BUFF_SIZE];
BYTE index;

void main()
{
     USART_Init();    //к примеру будем забирать из УСАРТа байты

     for(EVER)
    {
        
    }

#pragma vector=USART0_RXC_vect
  __interrupt void usart_rxc(void)
{
      BYTE sym;

      sym = UDR0;
      Buff[index++] = sym;
      index = index % BUFF_SIZE;
}
}

как написать подобное на АСМе?

пробовал написать следующее:
Код
...

.DSEG
USART_Index_H:  .byte 1
USART_Index_L:  .byte 1
USART_BUFF:  .byte 50

...
;инициализации и все такое
...

USART0_RXC:
  push ZH
  push ZL
  push r16

;грузим адрес нового элемента, полученный в предыдущем прерывании
  lds ZH,USART_Index_H
  lds ZL,USART_Index_L
        
  lds r16,UDR0
  st Z+,r16

;сохраняем новый адрес
  lds USART_Index_H,ZH
  lds USART_Index_L,ZL
  reti


но, полагаю, это работать не будет...

Забыл дописать
Код
...
;инициализация
lds ZH,high(USART_BUFF)
lds ZL,low(USART_BUFF)
sts USART_Index_H,ZH
sts USART_Index_L,ZL
...
;и забыл в конце прерывания дописать
pop r16
pop ZL
pop ZH
ILYAUL
Что бы понять до конца задачу - прерывания идут последовательно ? Т.е. 1, 2, 1, 2,1, 2 или нет 1111, 2, 11 ,2 и тд

HoboTech
Цитата(ILYAUL @ Jan 18 2011, 15:17) *
Что бы понять до конца задачу - прерывания идут последовательно ? Т.е. 1, 2, 1, 2,1, 2 или нет 1111, 2, 11 ,2 и тд


в общем задача сводится к тому, чтобы по приходу символа в USART заносить его последовательно в буфер
прерывание одно - USART0_RXC:
ILYAUL
Код
.def R16,temp
.def R17,count
.def R4,SaveSREG
... Инициализация

.DSEG
USART_BUFF:  .byte 50

SAVE_INDEX_Z: byte 2
          SaveZL =0
          SaveZH=1


MAIN:
ldi ZH,HIGH(USART_BUFF)
ldi ZL,LOW(USART_BUFF)
ldi count,50
.........................

USART0_RXC:
         in  SaveSREG,SREG
         push SaveSREG
         push temp
                                 push ZL
                                 push ZH
                                 lds  ZL,SAVE_INDEX_Z+SaveZL
                                 lds  ZH,SAVE_INDEX_Z+SaveZH
         lds  temp,UDR0
         st   Z+,temp
         dec count
         brne END_RX
         ldi ZH,HIGH(USART_BUFF)
         ldi ZL,LOW(USART_BUFF)
         ldi count,50
END_RX:
                                 sts  SAVE_INDEX_Z+SaveZL,ZL
                                 sts  SAVE_INDEX_Z+SaveZH,ZH
                                 pop ZH
                                 pop ZL
                     pop temp
                     pop SaveSREG
                     out SREG,SAVESREG
reti


Но лучше использовать регисты X или Y вместо Z- через Z идёт обращение к памяти програм и Eeprom и он чаще используется и соответственно его надо отслеживать и при необходимости
PUSH и POP
HoboTech
Я так понял Ваш код будет работать только в случае, когда в основном теле программы не изменяется содержимое ZH и ZL.
Меня интересует именно сохранение адреса, на который указывает регистровая пара Z после постинкремента, в переменную
Если, конечно, такое возможно
Дмитрий Ветра
Цитата(HoboTech @ Jan 18 2011, 17:39) *
Я так понял Ваш код будет работать только в случае, когда в основном теле программы не изменяется содержимое ZH и ZL.
Меня интересует именно сохранение адреса, на который указывает регистровая пара Z после постинкремента, в переменную
Если, конечно, такое возможно

Есть два варианта:


- сохраняем в ОЗУ;
.equ ZH_save 0x0100
.equ ZL_save 0x0101

sts ZH_save,ZH
sts ZL_ save,ZL

- копируем регистровую пару;

movw r3:r2,ZH:ZL

в целом:

lds ZH, ZH_save ; восстанавливаем указатель
lds ZL, ZL_save

in r16, UDR ; записываем принятый байт
st Z+, r16

sts ZH_save, ZH ; сохраняем указатель
sts ZL_save, ZL
HoboTech
Цитата(Дмитрий Ветра @ Jan 18 2011, 18:31) *
Есть два варианта:


- сохраняем в ОЗУ;
.equ ZH_save 0x0100
.equ ZL_save 0x0101

sts ZH_save,ZH
sts ZL_ save,ZL

- копируем регистровую пару;

movw r3:r2,ZH:ZL

в целом:

lds ZH, ZH_save ; восстанавливаем указатель
lds ZL, ZL_save

in r16, UDR ; записываем принятый байт
st Z+, r16

sts ZH_save, ZH ; сохраняем указатель
sts ZL_save, ZL


Спасибо, а то я разбаловался языками верхнего уровня, а на уровне "кирпичиков" топчусь как лось на одном месте sm.gif
ILYAUL
Цитата(HoboTech @ Jan 18 2011, 18:39) *
Я так понял Ваш код будет работать только в случае, когда в основном теле программы не изменяется содержимое ZH и ZL.
Меня интересует именно сохранение адреса, на который указывает регистровая пара Z после постинкремента, в переменную
Если, конечно, такое возможно

всё верно . Если Вы предполагаете изменять ZL ZH , то хотя бы напишите , что именно будет происходить в основной программе . Тогда будет понятно , каким образом сохранять Z . Толи записывать его в регистры хранения , толи в SRAM, а может будет достаточно push и pop

Я Вам переписал предыдущий пост- сохранив Вам Z для прерывания и для MAIN
HoboTech
И все таки туго что-то до меня доходит работа с адресами...
что означает следующий код:
Код
.DSEG
tmp: .byte 1

...

lds ZL,tmp

что я заношу в регистр ZL значение байта, который находился в памяти по адресу tmp или что я записываю значение младшего байта самого адреса? sad.gif
V_G
Это вы байт загружаете.
Адрес - другой командой (двумя для полного адреса):

LDI ZL, low(tmp)
LDI ZH,high(tmp)

Я подобные частые действия оформляю макросами, чтобы в одну строчку было
ILYAUL
Цитата(V_G @ Jan 21 2011, 15:22) *
Это вы байт загружаете.....

...... из SRAM в регистр ( Sorry V_G) так по понятнее будет. А про макросы наверное рановато
V_G
Как это в SRAM?
LDS - загрузка ИЗ SRAM в регистр (LoaD from Sram - мнемоники вполне работают)
ILYAUL
Цитата(V_G @ Jan 21 2011, 16:50) *
Как это в SRAM?
LDS - загрузка ИЗ SRAM в регистр (LoaD from Sram - мнемоники вполне работают)

Конечно , конечно - это я уже ,в конце недели зарапортовался. Поправил
_Bill
Цитата(HoboTech @ Jan 21 2011, 15:19) *
И все таки туго что-то до меня доходит работа с адресами...
что означает следующий код:
Код
.DSEG
tmp: .byte 1

...

lds ZL,tmp

что я заношу в регистр ZL значение байта, который находился в памяти по адресу tmp или что я записываю значение младшего байта самого адреса? sad.gif

В регистр ZL загружается содержимое ячейки памяти с адресом tmp. Если вы хотите загрузить в ZL адрес ячейки, используйте команду LDI. Все ваши проблемы возникают от того, что вы путаете различные методы адресации. В ассемблере AVR различные методы адресации определяются с помощью различных мнемоник.
Код
    ldi   r16, 5    ; Непосредственная (immediate) адресация - загрузка числа 5
    lds  r16, 5        ; Прямая (direct) адресация - загрузка в регистр r16 содержимого ячейки по адресу 5
    ld    r16, Z    ; Косвенная (indirect) адресация - загрузка в регистр r16 через указатель Z
    ld    r16, Z+     ; Автоинкрементная (post increment) адресация через указатель Z
    ld    r16, Z-      ; Автодекрементная (pre decrement) адресация через указатель Z
    ldd  r16, z+5    ;  Косвенная со смещением (inditect with displscement) адресация через указатель Z
                        ; адрес ячейки памяти определяется как сумма содержимого указателя Z и числа 5

Надеюсь, что немного прояснил ситуацию. Или нет?
HoboTech
Про lds и ldi я понял, спасибо. Не понял что именно загружается (значение или адрес) в этих строках:
Цитата(_Bill @ Jan 22 2011, 08:06) *
ld r16, Z ; Косвенная (indirect) адресация - загрузка в регистр r16 через указатель Z
ld r16, Z+ ; Автоинкрементная (post increment) адресация через указатель Z
ld r16, Z- ; Автодекрементная (pre decrement) адресация через указатель Z
ldd r16, z+5 ; Косвенная со смещением (inditect with displscement) адресация через указатель Z
; адрес ячейки памяти определяется как сумма содержимого указателя Z и числа 5[/code]


P.S. Мануалы честно читал, но когда дошло до практической реализации - запнулся.
_Pasha
Цитата(HoboTech @ Jan 24 2011, 12:42) *
P.S. Мануалы честно читал, но когда дошло до практической реализации - запнулся.

Вы считаете это чем-то из ряда вон, что при освоении нового материала надо приложить некоторые усилия?
demiurg_spb
Цитата(HoboTech @ Jan 24 2011, 11:42) *
Не понял что именно загружается (значение или адрес) в этих строках:

Для AVR в строках где нет никаких цифирей происходит косвенное обращение к данным (чтение или запись).
Адрес сидит внутри регистровой пары и его в коде операции не видно - поэтому такой тип адресации и называется косвенным (через индексные регистры X,Y,Z).
Смею предположить, что без такого способа адресации программирования, как мы его сегодня знаем, не существовало.
Так что изучите это очень внимательно! Основа основ как-никак:-)
ILYAUL
Цитата(_Bill @ Jan 22 2011, 09:06) *
В регистр ZL загружается содержимое ячейки памяти с адресом tmp. Если вы хотите загрузить в ZL адрес ячейки, используйте команду LDI. Все ваши проблемы возникают от того, что вы путаете различные методы адресации. В ассемблере AVR различные методы адресации определяются с помощью различных мнемоник.
Код
        ld    r16, Z-    ; Автодекрементная (pre decrement) адресация через указатель Z
Просто правка  LD R16 ,-Z
_Bill
Цитата(HoboTech @ Jan 24 2011, 12:42) *
Про lds и ldi я понял, спасибо. Не понял что именно загружается (значение или адрес) в этих строках:


P.S. Мануалы честно читал, но когда дошло до практической реализации - запнулся.

Методы адресации - это один из трех китов, на которых держится архитектура любого процессора (AVR в том числе). Так что стоит затратить время, чтобы разобраться с адресацией. В дальнейшем эти знания скорее всего вам понадобятся. Что касается семейства AVR, то советую внимательнее почитать сей документ Система команд AVR
ILYAUL
Можно , так же открыть Ефремова . Почитать главу "Способы адресации памяти данных"
HoboTech
Спасибо за ответы. Еще почитаю, наверняка возникнут вопросы, задам их тут.
нечитатель
Не считая полезным занятием переписывание собственными словами документа "AVR Instruction Set", обратил бы внимание на:
Код
ldi XH, high(addr)
ldi XL, low(addr)
cli                                  ; !!!
ld R16, X+
ld R17, X
sei                                  ; !!!

В том смысле, что если после ld R16, X+ произойдёт прерывание - после возврата из него XH и (или?) XL испортятся. Даже если попытано их сохранять/восстановлять в процедуре обработки прерывания.

Где-то умнее излагается о, но ссылку либо название затрудняюсь.
_Bill
Цитата(нечитатель @ Jan 26 2011, 04:54) *
Не считая полезным занятием переписывание собственными словами документа "AVR Instruction Set", обратил бы внимание на:
Код
ldi XH, high(addr)
ldi XL, low(addr)
cli                                 ; !!!
ld R16, X+
ld R17, X
sei                                 ; !!!

В том смысле, что если после ld R16, X+ произойдёт прерывание - после возврата из него XH и (или?) XL испортятся. Даже если попытано их сохранять/восстановлять в процедуре обработки прерывания.

Где-то умнее излагается о, но ссылку либо название затрудняюсь.

Это как?! 05.gif
V_G
Цитата(нечитатель @ Jan 26 2011, 10:54) *
если после ld R16, X+ произойдёт прерывание - после возврата из него XH и (или?) XL испортятся. Даже если попытано их сохранять/восстановлять в процедуре обработки прерывания.

Где-то умнее излагается о, но ссылку либо название затрудняюсь.

Если бы так было, это следовало бы писать красными большими буквами 32 кегля во всех документах, а Атмел давно бы заклевали конкуренты. Так что ссылку в студию, начнем клевать...

Может, вы спутали с загрузкой 2-байтных регистров с использованием внутренних защелок и жесткими требованиями к таймингам? Это есть, и во всех pdf-ках есть отдельный параграф про работу с 16-разрядными регистрами.
OlegNZH
Цитата(нечитатель @ Jan 26 2011, 03:54) *
В том смысле, что если после ld R16, X+ произойдёт прерывание - после возврата из него XH и (или?) XL испортятся. Даже если попытано их сохранять/восстановлять в процедуре обработки прерывания.

Где-то умнее излагается о, но ссылку либо название затрудняюсь.


sm.gif ну во-первых , после ld R16, X+ прерывание не может произойти , а во-вторых-что за диверсант будет портить регистр X ?

upd.Аааааа - V_G понял откуда ноги растут... sm.gif

Цитата...
Доступ к 16-разрядным регистрам

Регистры TCNTn, OCRnA/B/C и ICRn являются 16-разрядными, поэтому, доступ к ним через 8-разр. шину данных AVR ЦПУ может быть осуществлен с помощью двух инструкций чтения или записи. У каждого 16-разр. таймера имеется свой 8-разр. регистр для временного хранения старшего байта данных. Поэтому, во время доступа к 16-разр. регистрам одного таймера используется один и тот же временный регистр...

...Не все 16-разрядные регистры используют временный регистр для копирования старшего байта. Чтение 16-разр. регистров OCRnA/B/C не связано с использованием временного регистра...

...При этом следует обратить внимание на проблему, которая связана с необходимостью выполнения двух инструкций для получения доступа к 16-разр. регистру. Если после выполнения первой инструкции доступа 16-разр. регистра происходит прерывание и в процедуре обработки прерывания также происходит обновление этого же или другого регистра, но относящегося к тому же таймеру, то по завершении обработки прерывания изменяется содержимое временного регистра и выполнение второй инструкции приведет к некорректному результату. Таким образом, когда и в основной программе и в прерываниях происходит обновление временного регистра, то в основной программе перед инициацией доступа к 16-разр. регистру необходимо запретить прерывания...
Конец Цитаты.
_Bill
Цитата(OlegNZH @ Jan 26 2011, 09:04) *
sm.gif ну во-первых , после ld R16, X+ прерывание не может произойти , а во-вторых-что за диверсант будет портить регистр X ?

upd.Аааааа - я понял откуда ноги растут...

Ну, во-первых, прерывание может произойти когда угодно, но переход к прерыванию начинается только после выполнения текущей инструкции. Что касается доступа к 16-разрядным портам ВВ, то это совсем другая история.
OlegNZH
Цитата(_Bill @ Jan 26 2011, 08:46) *
Ну, во-первых, прерывание может произойти когда угодно, но переход к прерыванию начинается только после выполнения текущей инструкции. Что касается доступа к 16-разрядным портам ВВ, то это совсем другая история.

Вообще-то я- про конкретную конструкцию говорил
cli ; !!!
ld R16, X+
А регистры XYZ к прерываниям никаким боком...Здесь речь может идти только о регистрах ВВ.
_Bill
Цитата(OlegNZH @ Jan 26 2011, 10:02) *
Вообще-то я- про конкретную конструкцию говорил
cli ; !!!
ld R16, X+

На целостность выполнения инструкции ld R16, X+ запрет или разрешение прерываний никоим образом не повлияют. С таким же успехом можно использовать и такую конструкцию
Код
     sei                            ; !!!
     ld     R16, X+
ILYAUL
Цитата(OlegNZH @ Jan 26 2011, 09:02) *
Вообще-то я- про конкретную конструкцию говорил
cli ; !!!
ld R16, X+
А регистры XYZ к прерываниям никаким боком...Здесь речь может идти только о регистрах ВВ.

А где Вы в этом коде нашли регистры ВВ?
_Pasha
Цитата(нечитатель @ Jan 26 2011, 03:54) *
произойдёт прерывание - после возврата из него XH и (или?) XL испортятся. Даже если попытано их сохранять/восстановлять в процедуре обработки прерывания.

Это было 100 лет назад. До крызыса sm.gif Сейчас уже такой баги нетути.
_Bill
Цитата(_Pasha @ Jan 26 2011, 10:53) *
Это было 100 лет назад. До крызыса sm.gif Сейчас уже такой баги нетути.

Я что-то вообще о такой баге не слышал, да и встречать не приходилось. Или я не там ходил?
OlegNZH
Bill Да причём тут целостность инструкции! Уже второй пост.Кому вы что об'ясняете? Речь шла о том, что после постинкремента - уход по прерыванию , и сколько X не пушируй , он всё равно "сломается". И было указано , что с мерами предосторожностями нужно работать только с 16-ти битными регистрами ВВ , а не с XYZ.


Цитата(нечитатель @ Jan 26 2011, 03:54) *
Не считая полезным занятием переписывание собственными словами документа "AVR Instruction Set", обратил бы внимание на:
Код
ldi XH, high(addr)
ldi XL, low(addr)
cli                                ; !!!
ld R16, X+
ld R17, X
sei                                ; !!!

В том смысле, что если после ld R16, X+ произойдёт прерывание - после возврата из него XH и (или?) XL испортятся. Даже если попытано их сохранять/восстановлять в процедуре обработки прерывания.

Где-то умнее излагается о, но ссылку либо название затрудняюсь.
_Bill
Цитата(OlegNZH @ Jan 26 2011, 11:13) *
Bill Да причём тут целостность инструкции! Уже второй пост.Кому вы что об'ясняете? Речь шла о том, что после постинкремента - уход по прерыванию , и сколько X не пушируй , он всё равно "сломается".

Что значит "сломается"?
OlegNZH
Цитата(_Bill @ Jan 26 2011, 10:15) *
Что значит "сломается"?

Вот и я про то-же.(Вы шуток не понимаете sm.gif ) . В оригинале:
"...В том смысле, что если после ld R16, X+ произойдёт прерывание - после возврата из него XH и (или?) XL испортятся..."
ILYAUL
Цитата(OlegNZH @ Jan 26 2011, 10:13) *
Bill Речь шла о том, что после постинкремента - уход по прерыванию

Насколько понятно из Ваших слов это после этого
Код
ld R16, X+


Тогда не прокатывает вторая часть фразы-
Цитата
уход по прерыванию

Никуда он там не уйдёт , пока не выполнит
Код
ld R17, X
sei


И как же надо написать обработчик прерывания , что бы убить не только регистры XL и XH , но и найти в стеке ячейки с их даными и там их maniac.gif krapula.gif
OlegNZH
О,господи..... Вы мой пост #27 прочитайте. (и #26 V_G)
_Bill
Цитата(OlegNZH @ Jan 26 2011, 12:31) *
О,господи..... Вы мой пост #27 прочитайте. (и #26 V_G)

Прошу прощения. Сразу не понял. Случай из жизни (немного не в тему).
Как-то, еще во время учебы в институте, стоим на перемене, разговариваем, анекдоты травим. Рассказали анекдот, все посмеялись, звонок, все вернулись в аудиторию. Идет занятие. Вдруг по середине занятия раздается громкое "Гы-гы-гы!". Мы все дружно оглядываемся и удивленно-вопросительно смотрим на гогочущего. Тот удовлетворенно - "Дошло!". После этого заржали все остальные. Естественно, преподаватель так ничего и не понял.
ILYAUL
Цитата(OlegNZH @ Jan 26 2011, 11:31) *
О,господи..... Вы мой пост #27 прочитайте. (и #26 V_G)

Прочёл:
Цитата
ну во-первых , после ld R16, X+ прерывание не может произойти
На что Вам правда уже ответитли - может
К тому же эта фраза отличается от
Цитата
Речь шла о том, что после постинкремента - уход по прерыванию
на которую Вам я и ответил
нечитатель
Цитата(_Pasha @ Jan 26 2011, 09:53) *
Это было 100 лет назад. До крызыса :) Сейчас уже такой баги нетути.
С этого места заинтересовало. Бага была в чём конкретно и в какой момент перестала быть?

Популярное развлечение в плане для себя лично: забыть cli + sei вокруг её, танцевать с бубном на ровном месте.
Если не забыть - тогда и не танцевать, но после крызиса это как-то иначе теперь решать прилично мало ли вдруг.

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

---
К выступающим есть предложение не стараться делать так. Оно даже и в правилах отдельной строкой прописано.
_Pasha
Цитата(нечитатель @ Jan 26 2011, 23:52) *
С этого места заинтересовало. Бага была в чём конкретно и в какой момент перестала быть?

Первые эрраты на АТ90S2313 что-то подобное содержали, но это было так давно, что не помню, касалось ли это LD/ST , но LDS/STS - однозначно, типо такая конструкция
Код
sbrc  r0,0
lds   r1, ADDR

с разрешенными прерываниями, является багонеустойчивой
OlegNZH
Цитата(_Pasha @ Jan 27 2011, 07:32) *
Первые эрраты на АТ90S2313 что-то подобное содержали, но это было так давно, что не помню, касалось ли это LD/ST , но LDS/STS - однозначно, типо такая конструкция
Код
sbrc  r0,0
lds   r1, ADDR

с разрешенными прерываниями, является багонеустойчивой

Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.