Здравствуйте. Разбираюсь с ассемблером KEIL mVision5. Создал проект. Cortex M0. stm32f030. В С модуле определил переменную int Buf[200];
Создал ассемблерный файл. В нем определил ( вроде бы) переменную D. После чего просто пытаюсь записать r0 в переменную D и Buf всеми известными мне командами. Не срабатывает ни одна. Привожу листинг ассемблерного файла. В комментариях - ошибки, возникающие при компиляции.
EXTERN Buf EXPORT Fun
;задаю переменную D area MyData, Data D space 100
area MyCode, code
Fun ; пробую занести в регистры ссылки на переменные MOVS r2, D ; Expected constant or register expression MOVS r3, Buf ;Bad symbol, not defined or external
; еще пробую, теперь другой командой ADR r2, D ;Expected register relative expression ADR r3, Buf ;Target cannot be relocated. No suitable relocation exists for this instruction
; регистровая адресация со смещением (регистр считаю нулевым) STR r0, [r1, D] ;Expected register relative expression STR r0, [r1, Buf];Bad symbol, not defined or external
; просто регистровая адресация. На нее компилятор не ругается. А что толку, раз я не могу в регистры адрес загрузить STR r0, [r2] STR r0, [r3]
bx lr
END
Кто сможет пояснить, что где с чем не так, буду очень признателен. Кто будет писать про на фига тебе ассемблер, получит в глаз
Обычно начинают с чтения документации... Меню -> Help-> uVision help->ARM Development Tools - Compiler 5 User's Guides->Assemler User Guides Меню -> Help-> uVision help->ARM Development Tools - Compiler 6 User's Guides->Assemler User Guides
. После чего просто пытаюсь записать r0 в переменную D и Buf всеми известными мне командами.
Да потому что ассемблер наоборот работает. mov r2, #200 - это "записать число 200 в регистр r2". Справа налево. Но не наоборот. Слева (первым) стоит регистр НАЗНАЧЕНИЯ, а потом уже - источник - число или другой регистр. В этом случае команда mov ожидает константу в диапазоне 0-255. movs - это тоже самое, только суффикс s заставляет воздействовать на флаги N, Z операции. adr r2, N - это вообще псевдоинструкция, заносящая в регистр r2 адрес метки относительно счетчика инструкций (Program Counter). str работает наоборот - из первого берется и сохраняется втуда, куда указано во втором параметре.
Ну и надо изучить синтаксис компилятора ассемблера. А потом плюнуть на написание ассемблером. нафик оно вам
Обычно начинают с чтения документации... Меню -> Help-> uVision help->ARM Development Tools - Compiler 5 User's Guides->Assemler User Guides Меню -> Help-> uVision help->ARM Development Tools - Compiler 6 User's Guides->Assemler User Guides
Но вы можете продолжать методом_тыка...
Мой друг. Я рискую разрушить вашу картину мира... И нанести тяжелый удар вашей самооценке... Но я должен вам сообщить ужасную, ужасную новость... Мне право не ловко, но... знаете ли... Я понимаю, вам в это трудно поверить... Но... Вокруг вас не одни только идиоты! Блин. Чувак. Ну конечно же я читал документацию. Что ерунду-то нести!
Добавлено after 11 minutes 52 seconds: >> Да потому что ассемблер наоборот работает. mov r2, #200 - это "записать число 200 в регистр r2". Справа налево. Но не наоборот. Слева (первым) стоит регистр НАЗНАЧЕНИЯ, а потом уже - источник - число или другой регистр.
Так а я про что? Я вообще-то адрес в регистр пихаю. Конечно не наоборот.
>> mov r2, #200 - это "записать число 200 в регистр r2". Справа налево. Но не наоборот. Слева (первым) стоит регистр НАЗНАЧЕНИЯ, а потом уже - источник - число или другой регистр. В этом случае команда mov ожидает константу в диапазоне 0-255. movs - это тоже самое, только суффикс s заставляет воздействовать на флаги N, Z операции. adr r2, N - это вообще псевдоинструкция, заносящая в регистр r2 адрес метки относительно счетчика инструкций (Program Counter). str работает наоборот - из первого берется и сохраняется втуда, куда указано во втором параметре.
Выши познания восхищают. Но если б вы еще могли объяснить, почему ни одну из этих строк компилятор не пропускает без ошибки - цены б вам не было!
>> А потом плюнуть на написание ассемблером. нафик оно вам
А в глаз?
Добавлено after 5 minutes 5 seconds: Эх, умники... И хоть кто бы заметил, что я EXTERN и IMPORT в примере перепутал...
Ладно, я разобрался с проблемой. Оказывается, Keil ассемблер чувствителен к положению операторов на строке. В первый раз с таким встречаюсь. Но - факт остается фактом.
Эх, умники... И хоть кто бы заметил, что я EXTERN и IMPORT в примере перепутал...
Ладно, я разобрался с проблемой. Оказывается, Keil ассемблер чувствителен к положению операторов на строке. В первый раз с таким встречаюсь. Но - факт остается фактом.
Умничать многие любят, причём с двух сторон. А внимательности им обычно не хватает
А что до чувствительности к положению в строке... Мне кейловский ассемблер отчасти напоминает классический ассемблер для IBMовских мэйнфреймов (Система 360 и её потомки). Но тот создавался в первой половине 1960-х годов, и весьма жёсткие требования к позиционированию операторов облегчали транслятору работу. Но вот почему так сделала ARM в 1980-х -- сия тайна велика есть...
Почему-то этих тем целых две. Вот вторая. Я вчера там ответил как загрузить адрес в регистр. Повторю здесь. Для загрузки адреса переменной D в регистр используйте синтакс ldr R0, =D т.е. со знаком равенства перед переменной.
>> Но вот почему так сделала ARM в 1980-х -- сия тайна велика есть...
Ага... То есть все ассемблеры разных производителей используют компилятор, который им предоставили ARM?
Добавлено after 1 minute 8 seconds: Ну да, проблема решена. Все упиралось в расположение операторов на строке.
Добавлено after 5 minutes 17 seconds: >> Умничать многие любят, причём с двух сторон.
Да наверняка. Но меня бесит эта особенность отечественных форумов(в англоязычных такого сильно меньше), что при любом вопросе тебе сначала прилетит некоторое количество понтов не в тему. И только потом, если до этого дойдет очередь (что не факт, иногда все так и тонет в обсуждении вопросов, которых ни кто не задавал. Типа, стоит писать на ассемблере или нет ), только потом может прийти что-нибудь дельное. Я даже пару раз пари заключал, что так и будет. Выигрывал, ясно дело.
Я его побоялся, предположив отсутствие достаточной документации, в первую очередь как раз на ассемблер. При работе с Atmel с этим были некоторые проблемы. Кстати, интересно - а Kail позволяет вставлять ассемблерные вставки непосредственно в С?
Типа, стоит писать на ассемблере или нет ), только потом может прийти что-нибудь дельное. Я даже пару раз пари заключал, что так и будет. Выигрывал, ясно дело.
Ну я весьма неплохо знаю ассемблер для Cortex M7, но вот чтобы писать на нем полностью - это уж упаси бог. Не больше чем ассемблерные вставки, да и то, ну ево нафик. Я даже могу заключить с вами пари, что вы не сможете за разумное время написать чтото более-менее сложное на голом ассемблере. Сишный компилятор со включенной оптимизацией это сделает не хуже, зато с меньшими затратами. А знаете ли вы, что одна и та же инструкция может иметь как 16-битный, так и 32-битный ее вариант с суффиксом .w, или знаете ли как правильно использовать it-блок, или же помните наизусть все суффиксы условного исполнения? В частности, можете тогда объяснить, зачем вы написали movs вместо mov? Понимаю, что вы покачто не дошли до этого. (s ставится для того, чтобы после этой инструкции использовать ветвление по условию флагов) ARM - это не AVR, и старый подход тут не шибко то канает. Не стоит слишком грузиться написанием на ассемблере, достаточно просто понять, как оно работает и чего делает.
Ага... То есть все ассемблеры разных производителей используют компилятор, который им предоставили ARM?.
с точностью до наоборот. Язык ассемблера ARM - это мнемноника (буковки, обозначающие инструкции) которого предоставлена ARM-ом. А компилятор (средство, превращающиее буковки в маш.коды) может делать кто угодно и добавлять в него свои плюшки, упрощающие написание - метки, макросы, выражения, модульные связи. Например, =D - такого в ассемблере АРМ нету. Но компилятор может предоставть D как текстовую метку для числового выражения. Можно даже вообще написать свою мнемонику ассемблера (в т.ч. и на русском языке) и компилятор к ней, лишь бы на выходе генерировались правильные маш.коды ARM.
Кстати, интересно - а Kail позволяет вставлять ассемблерные вставки непосредственно в С?
Позволяет. Об этом лучше действительно прочитать во встроенном в Keil мануале (секция 10), как dosicus советовал выше. Там куча ограничений. Если нужен ASM в проекте на С, по-моему удобнее просто написать отдельную асемблерную функцию. Тогда следует лишь позаботиться о сохранении некоторых регистров, используемых в ней. Новичкам ASM для ARM рекомендую почитать отличную книгу. После неё все вопросы заданные здесь будут сняты. Ещё в качестве примера можете посмотреть исходники одного из моих проектов, написанного полностью на ASMe, правда, для другого АРМа.
Я тоже так думал. А потом оказалось что инструкции выполняются не за один такт (все!!!), инструкция которая выполняется дольше остальных - блокирует конвейер процессора в случае мгновенного использования тех-же регистров, инструкции переходов не выполняют предварительного чтения кода по адресу, в ряде случаев несколько дополнительных регистров в пространстве функции -многократно увеличивают скорость её работы. И так далее. А кроме конвейера ядра, есть ещё FIFO конвейер шины данных/адреса, с весьма специфичным арбитром доступа. Ещё, для быстрых чипов используются домены синхронизации между шинами с разной скоростью, которые добавляют лагов в случае неумелого использования.
А потом оказывается что программа на С++ выполняется быстрее чем код набранный в асме.
>> ARM - это не AVR, и старый подход тут не шибко то канает. Не стоит слишком грузиться написанием на ассемблере, достаточно просто понять, как оно работает и чего делает.
Это общий подход на форумах- придумать для спрашивающего за него, что ему лучше, и потом говорить о чем-то совсем другом, чем то, надо спрашивающему. Ну, на примере с ассемблером. Раз я готов мучаться, значит, мне это зачем-нибудь нужно? Согласитесь.
>> Например, =D - такого в ассемблере АРМ нету.
Да, кстати - а что такое =D? Я так понимаю, взятие адреса? А просто D? видимо, будет обращением к памяти? Типа, LDR R0, D.
Ser60, спасибо за книжку и за исходник.
Добавлено after 20 minutes 45 seconds: >> инструкция может иметь как 16-битный, так и 32-битный ее вариант с суффиксом .w
Это, я так понимаю, не относится к Cortex MO? Или они такое тоже умеют?
LDR - это 32-битная инструкция, и адрес переменных тоже 32-битный. Поэтому адрес полностью не влезет в инструкцию. Конструкция типа =D позволяет компилятору создать область в пространстве кода между функциями и разместить туда значение адреса переменной D, чтобы из кода в инструкции LDR R0, =D обратиться к размещённому адресу относительно PC (Program Counter). Таким образом, вместо LDR, R0, =D в коде будет команда LDR, R0, [PC, #80], где 80 будет вычисленное компилятором смещение (в реальной программе это смещение может быть другим). Инструкция типа LDR R0, D просто не скомпилируется, посколько в формате команды LDR не предусмотрено в качестве второго операнда писать что-то отличное от [Rn, {#offset}]. Прочтите книгу или туториал где-либо в Инете - много вопросов отпадёт.
LDR - это 32-битная инструкция, и адрес переменных тоже 32-битный. Поэтому адрес полностью не влезет в инструкцию. Конструкция типа =D позволяет компилятору создать область в пространстве кода между функциями и разместить туда значение адреса переменной D, чтобы из кода в инструкции LDR R0, =D обратиться к размещённому адресу относительно PC (Program Counter). Таким образом, вместо LDR, R0, =D в коде будет команда LDR, R0, [PC, #80], где 80 будет вычисленное компилятором смещение (в реальной программе это смещение может быть другим). Инструкция типа LDR R0, D просто не скомпилируется, поскольку в формате команды LDR не предусмотрено в качестве второго операнда писать что-то отличное от [Rn, {#offset}]. Прочтите книгу или туториал где-либо в Инете - много вопросов отпадёт.
Да вот нет, не совсем так. Компилятор LDR R0, D пропускает без ошибок. Матерится уже то ли линковщик, то ли второй проход :
Cube\Cube.axf: Error: L6286E: Relocation #REL:0 in addasm.o(MyCode) with respect to Buf. Value(0x5ffffa6) out of range(-1 - 0xfe) for (R_ARM_THM_PC8)
полагаю, компилятор подставляет какие-то коды, которые потом уже не проходят проверку.
и да - после книги или туториал многие вопросы отпали. А многие - нет. Иначе я не стал бы тут вас домогаться, поверьте мне...
Боже, что я написал. Инструкция LDR, R0, D подразумевает D как оффсет относительно PC, т.е. в R0 загрузится PC+D. В этом случае D должно должно быть в пределах от −4095 до 4095, т.к. для более длинных констант нет места в инструкции. Насчёт IT-блока, лучше в книге посмотрите. Здесь перепиисывать долго.
Последний раз редактировалось Ser60 Сб фев 15, 2020 04:13:16, всего редактировалось 2 раз(а).
:facepalm: Боже, что я написал. Инструкция LDR, R0, D подразумевает D как оффсет относительно PC, т.е. в R0 загрузится PC+D. В этом случае D не должно превосходить 0xFFF, т.к. для более длинных констант нет места в инструкции.
Так. То есть = подсказывает компилятору, что надо использовать псевдоинструкцию... А если ее нет, то используется синтаксис обк ADR. Ага... Ясно.
Добавлено after 13 minutes 8 seconds: >> Насчёт IT-блока, лучше в книге посмотрите.
Ну, на примере с ассемблером. Раз я готов мучаться, значит, мне это зачем-нибудь нужно? Согласитесь.
Ну конечно вы можете помучаться, вероятно вам кажется что сейчас это позарез нужно. Пожалста, не возражаю! Я тоже был таким в свое время. Пересев на STM32 я первым делом, как и для AVR/PIC, засел за ассемблер. Ну и чо? Пролистав кучу страниц, я толком не понял, с какого адреса должна начинаться прога. Потом оказалось, что с того адреса, какой будет записан в векторе сброса. Параллельно делал на сях. В результате послал этот ассемблер впень. С тех пор ассемблер видел ну разве что в ассемблерных вставках в ядре RTOS. Ассемблер можно знать и понимать. Но писать на нем в наше время... Не понимаю тех, кто пытается в 2020 году писать на ассемблере для АРМ. Ну если только вы не разрабатываете какую-нить военно-космическую байду. Да и то, риск "человеческой" ошибки выше чем риск ошибки компилятора.
IT блок (if-then) - это набор инструкций для компактного создания исполнения (ветвления) по условию, аналог сишного switch. Для Cortex M0 не актуально. Cortex M0 вообще самый урезанный по набору инструкций.
Чтобы глубже проникнуться форматом операндов в инструкциях, надо посмотреть из чего вообще состоит инструкция в побитовом представлении:
после этого вы будете понимать, почему некоторые инструкции (или вариант инструкции) не имеют доступа к регистрам выше r7.
Вообще, начните с чисто числовых абстрактных выражений, не привязываясь к внешним сишным модулям. Поймите как работает та или иная инструкция, как выполняются последовательности инструкций, как воздействовать на флаги результата выполнения и как использовать эти флаги для создания ветвлений или условий исполнения. Потом легче будет понимать, что вообще нужно подсовывать во всякие D, чтобы инструкция переваривала басурманские буковки.
Сейчас этот форум просматривают: kolobok0 и гости: 18
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения