Почему было использовать физические адреса ОЗУ и записывать во флеш, а не по виртуальному адресу.
Зачем для отображения флэш использовать ОЗУ? Его и так мало в контроллере, обычно даже меньше чем флэш. ОЗУ используется для данных, а таблица векторов прерываний, обычно, только для чтения. Если уж хочется "на лету" менять что-то в таблице векторов прерываний, то в вашем контроллере можно перенести её в ОЗУ с помощью регистра VTOR.
Что вы хотите получить отображением флэш на ОЗУ вообще не понятно...
Да действительно. Посмотрел nm'ом бинарники для ардуины, там адреса начинаются с близких к нулю. А вот вывод от блинка из Eclipse для stm32f4 Спойлер08001894 W __aeabi_idiv0 08001894 W __aeabi_ldiv0 08001594 T __aeabi_uldivmod 20000080 b __bss_begin_guard 20000128 B __bss_end__ 20000124 b __bss_end_guard 0800041c D __bss_regions_array_end 0800040c D __bss_regions_array_start 20000080 B __bss_start__ 20000000 d __data_begin_guard 20000080 D __data_end__ 2000007c d __data_end_guard 0800040c D __data_regions_array_end 080003f4 D __data_regions_array_start 20000000 D __data_start__ 08001898 T __errno 080024f8 D __etext 080024f8 R __exidx_end 080024f0 R __exidx_start 0800041c d __fini_array_end 0800041c d __fini_array_start 0800041c d __init_array_end 0800041c d __init_array_start 08001100 W __initialize_args 08001448 T __initialize_hardware 080012e4 W __initialize_hardware_early 08000000 T __isr_vectors 20000120 B __lock___malloc_recursive_mutex 2000fc00 A __Main_Stack_Limit 00000400 A __Main_Stack_Size 20000118 B __malloc_free_list 0800223c T __malloc_lock 2000011c B __malloc_sbrk_start 08002248 T __malloc_unlock 0800041c d __preinit_array_end 0800041c d __preinit_array_start 08002264 T __retarget_lock_acquire_recursive 08002266 T __retarget_lock_release_recursive 08001928 T __ssputs_r 20010000 T __stack 080015c4 T __udivmoddi4 08000000 A __vectors_start 08000000 A __vectors_start__ 20000128 B _ebss 20000080 D _edata 20000128 B _end_noinit 20010000 A _estack 080024f8 D _etext 080010b8 W _exit 08002000 T _free_r 20000128 B _Heap_Begin 2000fc00 A _Heap_Limit 20000018 D _impure_ptr 080020d4 T _malloc_r 08002254 T _malloc_usable_size_r 00000100 A _Minimum_Stack_Size 20000128 B _noinit 08001be0 T _printf_common 08001cbc T _printf_i 080021bc T _realloc_r 080010bc T _sbrk 0800221c T _sbrk_r 20000080 B _sbss 20000000 D _sdata 080024f8 A _sidata 08000194 W _start 080019e0 T _svfiprintf_r 080019e0 T _svfprintf_r 08001110 t _trace_write_semihosting_debug 080018b4 T _vsniprintf_r 080018b4 T _vsnprintf_r 080003f0 W ADC_IRQHandler 08002444 T AHBPrescTable 2000000c d argv.1 08001458 T assert_failed 08001494 T blink_led_init 08001480 T blink_led_off 0800146c T blink_led_on 20000094 b buf.0 08000344 W BusFault_Handler 08000358 W BusFault_Handler_C 20000088 b current_heap_end.0 080003d8 W DebugMon_Handler 080003f0 W Default_Handler 080003f0 W DMA1_Stream0_IRQHandler 080003f0 W DMA1_Stream1_IRQHandler 080003f0 W DMA1_Stream2_IRQHandler 080003f0 W DMA1_Stream3_IRQHandler 080003f0 W DMA1_Stream4_IRQHandler 080003f0 W DMA1_Stream5_IRQHandler 080003f0 W DMA1_Stream6_IRQHandler 080003f0 W DMA1_Stream7_IRQHandler 080003f0 W DMA2_Stream0_IRQHandler 080003f0 W DMA2_Stream1_IRQHandler 080003f0 W DMA2_Stream2_IRQHandler 080003f0 W DMA2_Stream3_IRQHandler 080003f0 W DMA2_Stream4_IRQHandler 080003f0 W DMA2_Stream5_IRQHandler 080003f0 W DMA2_Stream6_IRQHandler 080003f0 W DMA2_Stream7_IRQHandler 080011d0 T dumpExceptionStack 2000008c B errno 080003f0 W EXTI0_IRQHandler 080003f0 W EXTI1_IRQHandler 080003f0 W EXTI15_10_IRQHandler 080003f0 W EXTI2_IRQHandler 080003f0 W EXTI3_IRQHandler 080003f0 W EXTI4_IRQHandler 080003f0 W EXTI9_5_IRQHandler 080003f0 W FLASH_IRQHandler 080003f0 W FPU_IRQHandler 080004b8 W HAL_GetTick 080005f0 T HAL_GPIO_Init 08000894 T HAL_GPIO_WritePin 080004a0 W HAL_IncTick 0800046c T HAL_Init 08000420 W HAL_InitTick 08001524 T HAL_MspInit 08000500 T HAL_NVIC_SetPriority 080004c4 T HAL_NVIC_SetPriorityGrouping 08000e90 T HAL_RCC_ClockConfig 080010ac T HAL_RCC_GetHCLKFreq 08000dbc W HAL_RCC_GetSysClockFreq 080008cc W HAL_RCC_OscConfig 080005b4 T HAL_SYSTICK_CLKSourceConfig 0800058c T HAL_SYSTICK_Config 080002ce W HardFault_Handler 080002e2 W HardFault_Handler_C 080003f0 W I2C1_ER_IRQHandler 080003f0 W I2C1_EV_IRQHandler 080003f0 W I2C2_ER_IRQHandler 080003f0 W I2C2_EV_IRQHandler 080003f0 W I2C3_ER_IRQHandler 080003f0 W I2C3_EV_IRQHandler 2000001c d impure_data 080012c4 T isSemihosting 080014cc T main 08001f10 T memchr 08001fb0 T memcpy 08000340 W MemManage_Handler 08001fcc T memmove 20000090 b name.0 080002ca W NMI_Handler 080003f0 W OTG_FS_IRQHandler 080003f0 W OTG_FS_WKUP_IRQHandler 080003dc W PendSV_Handler 080003f0 W PVD_IRQHandler 080003f0 W RCC_IRQHandler 080002c4 T Reset_Handler 080003f0 W RTC_Alarm_IRQHandler 080003f0 W RTC_WKUP_IRQHandler 08002094 t sbrk_aligned 080003f0 W SDIO_IRQHandler 080003f0 W SPI1_IRQHandler 080003f0 W SPI2_IRQHandler 080003f0 W SPI3_IRQHandler 080003f0 W SPI4_IRQHandler 080018a4 T strlen 080003d4 W SVC_Handler 080013ac W SystemClock_Config 20000014 D SystemCoreClock 0800130c T SystemCoreClockUpdate 080012fc T SystemInit 08001588 T SysTick_Handler 080003f0 W TAMP_STAMP_IRQHandler 080003f0 W TIM1_BRK_TIM9_IRQHandler 080003f0 W TIM1_CC_IRQHandler 080003f0 W TIM1_TRG_COM_TIM11_IRQHandler 080003f0 W TIM1_UP_TIM10_IRQHandler 080003f0 W TIM2_IRQHandler 080003f0 W TIM3_IRQHandler 080003f0 W TIM4_IRQHandler 080003f0 W TIM5_IRQHandler 20000114 B timer_delayCount 08001560 T timer_sleep 08001528 T timer_start 08001574 T timer_tick 0800117c T trace_printf 080011b0 T trace_puts 08001172 T trace_write 0800038c W UsageFault_Handler 080003a0 W UsageFault_Handler_C 080003f0 W USART1_IRQHandler 080003f0 W USART2_IRQHandler 080003f0 W USART6_IRQHandler 20000084 B uwTick 20000004 D uwTickFreq 20000008 D uwTickPrio 0800190c T vsniprintf 0800190c T vsnprintf 080003f0 W WWDG_IRQHandler
Самый обычный просмотрщик стандартного hex-файла. Открываем в блокноте этот файл и читаем первую строчку: :020000040800F2 выделенное - и есть адрес (его старшие байты) начала размещения данных.
Возможно, но лучше через nm (обычный тоже пойдёт) сравнить два бинарника для ардуины и stm32. Там видна разница в адресах, и видно что адреса вписаны в бинарник при компиляции.
Что вы хотите получить отображением флэш на ОЗУ вообще не понятно...
Может быть, псевдоОС с выполнением пользовательских "программ"?
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Почему было использовать физические адреса ОЗУ и записывать во флеш, а не по виртуальному адресу.
Зачем для отображения флэш использовать ОЗУ? Его и так мало в контроллере, обычно даже меньше чем флэш. ОЗУ используется для данных, а таблица векторов прерываний, обычно, только для чтения. Если уж хочется "на лету" менять что-то в таблице векторов прерываний, то в вашем контроллере можно перенести её в ОЗУ с помощью регистра VTOR.
Что вы хотите получить отображением флэш на ОЗУ вообще не понятно...
Было непонятно зачем использовать адресное пространство большее чем ОЗУ. Но может быть действительно так удобнее всё держать в одном адресном пространстве и читать из флеша как из ОЗУ, чем использовать интерфейс вроде файла или EEPROM.
Было непонятно зачем использовать адресное пространство большее чем ОЗУ.
Потому что 32 бита позволяют адресовать 4 ГБ. К некоторым моделям STM32 можно подключать внешнюю память на сотни МБ которая доступна из этого адресного пространства.
ddr4 писал(а):
Но может быть действительно так удобнее всё держать в одном адресном пространстве
Удобнее. Например передаешь данные в функцию по указателю и не важно они во флеше или в ОЗУ. В AVR так не получится.
1. Можно вот так код в Compiler explorer. Как так получается предлагаю самому подумать. 2. В IAR есть модификатор __generic для указателей, которые могут указывать как на RAM, так и на flash. Они чуть медленнее обычных работают, но работают.Спойлер
Это не совсем то о чем я пишу. Здесь компилятор учитывает где расположены данные. В случае адресного пространства как у STM32 в этом нет необходимости.
Хотелось бы очистить стандартный ld-script Эклипса от очень полезных, но непонятных секций.
Вот например секции ниже, мне для uart_print ("Hello world.") - примера не потребуются? Спойлер
Код:
/* * Used for validation only, do not allocate anything here! * * This is just to check that there is enough RAM left for the Main * stack. It should generate an error if it's full. */ ._check_stack : ALIGN(4) { . = . + _Minimum_Stack_Size ; } >RAM
/* * The FLASH Bank1. * The C or assembly source must explicitly place the code * or data there using the "section" attribute. */ .b1text : ALIGN(4) { *(.b1text) /* remaining code */ *(.b1rodata) /* read-only data (constants) */ *(.b1rodata.*) } >FLASHB1
/* * The EXTMEM. * The C or assembly source must explicitly place the code or data there * using the "section" attribute. */
Также непонятно назначение секции .inits, в некоторых примерах её нет. Можно ли удалить, чтобы глаза не мозолила? Спойлер
Код:
.inits : ALIGN(4) { /* * Memory regions initialisation arrays. * * Thee are two kinds of arrays for each RAM region, one for * data and one for bss. Each is iterrated at startup and the * region initialisation is performed. * * The data array includes: * - from (LOADADDR()) * - region_begin (ADDR()) * - region_end (ADDR()+SIZEOF()) * * The bss array includes: * - region_begin (ADDR()) * - region_end (ADDR()+SIZEOF()) * * WARNING: It is mandatory that the regions are word aligned, * since the initialisation code works only on words. */
/* End of memory regions initialisation arrays. */
/* * These are the old initialisation sections, intended to contain * naked code, with the prologue/epilogue added by crti.o/crtn.o * when linking with startup files. The standalone startup code * currently does not run these, better use the init arrays below. */ KEEP(*(.init)) KEEP(*(.fini))
. = ALIGN(4);
/* * The preinit code, i.e. an array of pointers to initialisation * functions to be performed before constructors. */ PROVIDE_HIDDEN (__preinit_array_start = .);
/* * Used to run the SystemInit() before anything else. */ KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))
/* * Used for other platform inits. */ KEEP(*(.preinit_array_platform .preinit_array_platform.*))
/* * The application inits. If you need to enforce some order in * execution, create new sections, as before. */ KEEP(*(.preinit_array .preinit_array.*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* * The init code, i.e. an array of pointers to static constructors. */ PROVIDE_HIDDEN (__init_array_start = .); KEEP(*(SORT(.init_array.*))) KEEP(*(.init_array)) PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* * The fini code, i.e. an array of pointers to static destructors. */ PROVIDE_HIDDEN (__fini_array_start = .); KEEP(*(SORT(.fini_array.*))) KEEP(*(.fini_array)) PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
Также в Экплипсе секция .rodata находится внутри секции .text, а в других примерах .rodata самостоятельная секция. Что лучше?
Также непонятно зачем нужны разделы типа .bss_CCMRAM (NOLOAD) и .noinit_CCMRAM (NOLOAD) - вторичные неинициализированные секции данных ? Можно ли удалить ?
Спойлер
Код:
/* The secondary uninitialised data section. */ .bss_CCMRAM (NOLOAD) : ALIGN(4) { *(.bss.CCMRAM .bss.CCMRAM.*) } > CCMRAM
А ниже в нём, это пользовательские секции? то есть не обязательные?
Эти секции создаёт компилятор. В них содержится информация, необходимая стартовому коду для инициализации глобального окружения. Посмотрите .map файл, они там есть.
Откуда у вас такое маниакальное желание что-то удалить, даже не понимая зачем это нужно? Всё что вам нужно сделать с этим скриптом - поставить правильный размер FLASH и SRAM для вашего контроллера. Если в скрипте описаны какие-то незадействованные в коде секции, то их всё равно не будет в итоговой прошивке. Это всего лишь правила, по которым линкер размещает их в адресном пространстве, при их наличии в коде. Они жрать не просят. Ничего лишнего в загруженном мной скрипте нет, займитесь уже программированием.
Ни в чём, воспринимайте это как имя области памяти, примерно как имя переменной в программе. Можете назвать как хотите, чтобы даже враг не догадался что это
Кстати если я заменю все (в том числе в .isr_vertors ) "> FLASH" на "> SRAM" это будет работать?
Будет. При отладке программы в SRAM так и делают. Посмотрите приложенный скрипт и сравните с тем что я первым выложил. Вот этот настроен на загрузку кода для отладки в SRAM.
Ответьте на простой вопрос - ЗАЧЕМ? Зачем вы пытаетесь не рассказать линкеру что делать с "кучей", если она появится в коде? Эти строки в скрипте жрать просят?
Они не влияют ни на размер прошивки, ни на скорость ее работы.
То есть если я удалю множество неиспользуемых секций, а в остальных заменю >FLASH на >RAM, скорость запуска приложения не изменится?
VladislavS писал(а):
Посмотрите .map файл, они там есть.
У меня нет такого файла.
VladislavS писал(а):
Откуда у вас такое маниакальное желание что-то удалить, даже не понимая зачем это нужно?
Чтобы оставить только то что нужно, раз STM32 в отличии от AVR-gcc предоставляет возможность управления запуском, то значить это нормально удалять всё что не нужно. Пытаюсь упростить код по максимуму. Для меня этот Blink с кучей файлов выглядит сложно. "Хеллоу Ворлд" будет ещё сложней.
VladislavS писал(а):
При отладке программы в SRAM так и делают. Посмотрите приложенный скрипт и сравните с тем что я первым выложил. Вот этот настроен на загрузку кода для отладки в SRAM.
Да всё заменено на SRAM, то есть получается в случае замены FLASH на RAM, при запуске программы секции размещаются в оперативке, а до этого размещались во FLASH ?
Ответьте на простой вопрос - ЗАЧЕМ? Зачем вы пытаетесь не рассказать линкеру что делать с "кучей", если она появится в коде? Эти строки в скрипте жрать просят?
Если у меня в коде нет malloc(), надеюсь в этой куче обёрток её тоже нет. Поэтому мне куча и не нужна. В Арудине я malloc() не использовал, то и здесь не требуется.
То есть если я удалю множество неиспользуемых секций, а в остальных заменю >FLASH на >RAM, скорость запуска приложения не изменится?
Что такое в вашем понимании скрость запуска приложения? При включении код лежит в флэш и с первых же тактов начинает работать c Reset_Handler. Это и есть приложение. Где тут скорость запуска измерить. А изменится то, что оно либо не соберётся, либо не будет работать.
Он создаётся при компиляции при указании соответствующего ключа. Вот это, в отличии от ковыряния скриптов, стоит сделать. Там много интересной информации.
Чтобы оставить только то что нужно, раз STM32 в отличии от AVR-gcc предоставляет возможность управления запуском, то значить это нормально удалять всё что не нужно. Пытаюсь упростить код по максимуму. Для меня этот Blink с кучей файлов выглядит сложно. "Хеллоу Ворлд" будет ещё сложней.
Ну так и ковыряйте программу, чего вы до линкерскрипта то докопались? Он лишь задаёт правила размещения того что есть в коде. Всё что есть в коде должно быть размещено в памяти. Чего нет, того и так нет. Линкерскрипт вообще никак не влияет на упрощение программы.
Да всё заменено на SRAM, то есть получается в случае замены FLASH на RAM, при запуске программы секции размещаются в оперативке, а до этого размещались во FLASH ?
Да. Только вы должны понимать, что сама по себе программа в оперативке не появится. Кто-то или что-то должны её туда перед запуском поместить.
Если у меня в коде нет malloc(), надеюсь в этой куче обёрток её тоже нет. Поэтому мне куча и не нужна. В Арудине я malloc() не использовал, то и здесь не требуется.
Ну и не используйте дальше. От того что вы упомянете её в скрипте линкера она в коде не появится.
Что такое в вашем понимании скрость запуска приложения? При включении код лежит в флэш и с первых же тактов начинает работать c Reset_Handler. Это и есть приложение.
Я же не знаю как оно в реальности запускается, возможно в линкере указывается куда МК (операционная система) должна разместить секции программы после её запуска. На деле МК размещает секции по адресам в адресном пространстве, желательно чтобы МК размещал секции в ОЗУ, так как во-первых ОЗУ быстрей FLASH'a, а значит скорость программы возрастает, а во-вторых FLASH имеет ограниченный ресурс на число записей. То есть МК с размещением секций в ОЗУ проживёт дольше. Но видимо указание линкеру разместить секцию в >RAM, ещё не является гарантией что она будет находится именно в ОЗУ ..? Так как нельзя сказать точно на какое устройство отражается кусок адресного пространства.
VladislavS писал(а):
Он создаётся при компиляции при указании соответствующего ключа. Вот это, в отличии от ковыряния скриптов, стоит сделать. Там много интересной информации.
Спасибо. Действительно интересная информация.
VladislavS писал(а):
Да. Только вы должны понимать, что сама по себе программа в оперативке не появится. Кто-то или что-то должны её туда перед запуском поместить.
В коде стартапа секция данных копируется по адресу (не факт что ОЗУ), а секция bss зануляется. Остальные секции возможно размещает сам МК, может у него есть какие-то внутренние механизмы?
VladislavS писал(а):
Ну и не используйте дальше. От того что вы упомянете её в скрипте линкера она в коде не появится.
В коде целый файл sbrk.c под подготовку кучи, + в .map-файле heap'a навалено. Вроде оно всё не мешает, но когда этого всего "не мешает" - много, оно как-то начинает мешать.
Последний раз редактировалось ddr4 Сб авг 27, 2022 17:54:56, всего редактировалось 1 раз.
Вот вы подключили библиотеку с функциями, струкурами, дефайнами и др. и почти все их не используете. Будете выпиливать не задействованные и без разницы что компиль это сам может сделать? С секциями тоже самое. Если не используются, они не попадут в прошивку (при адекватных ключах компиля и линкера). А если используются, прошивку не соберете.
ddr4 писал(а):
То есть если я удалю множество неиспользуемых секций
Если секции действительно не используются то просто зря потратите время, т. к. они все равно в прошивку не попадут (повторюсь, при правильных ключах компиля и линкера).
ddr4 писал(а):
а в остальных заменю >FLASH на >RAM, скорость запуска приложения не изменится?
Загружать прошивку как планируете? Обычно прошивку помещают в ОЗУ при отладке (ее загружает отладчик) или необходимости полностью переписать флешь.
ddr4 писал(а):
У меня нет такого файла.
Создается при компиляции.
ddr4 писал(а):
значить это нормально удалять всё что не нужно.
Нормально это когда этим занимается компиль вместе с линкером. Но вы упорно хотите выполнять их работу!
ddr4 писал(а):
Для меня этот Blink с кучей файлов выглядит сложно. "Хеллоу Ворлд" будет ещё сложней.
Какая разница сколько файлов в проекте?
ddr4 писал(а):
В Арудине я malloc() не использовал
То есть строками не пользовались, print в любом виде не использовали, классы динамически не создавали и много чего другого не делали что выделяло память из кучи?
С секциями init_array - даже у меня вопросы имеются. Применительно к ARM, потому как для компов может быть всё иначе. Для собирательных секций data, rodata, bss, text - есть чёткое определение в доках GCC, чего и в каком качестве туда помещается. Для остальных секций выполняется простое условие, имя секции должно совпадать с именем объекта (функция, переменная, чистая/грязная память, константы и вектора). Секции array - тоже собирательные, потому как объектов с похожим названием нет ни в одном проекте, и даже в самом GCC. Однако точного описания в доках GCC нету. (прям именно там, на сайте GCC). Отчего у меня лично возникает ощущение что секцию забыли удалить. Без неё код собирается, и даже не меняет свой состав на бинарном уровне.
Есть вариант что оно висит (или раньше было там) на уровне спецификаций (*.specs) - код подключаемый вне желания программиста. Изначально спецификации были предназначены для того чтобы у всех всё было одинаково. Но случилась неприятность, и бардак возник за забором - чипов и их модификаций развелось как собак не резаных. Писать спеки на каждый чих ARM зоопарка - желающих не нашлось. Отчего настройки спустились на пользовательский уровень, а в *.specs осталась заглушка.
Если не нравится что-то, просто поочередно удаляйте секцию за секцией и смотрите, когда при очередном компилировании ошибки полезут. Заодним и разберетесь, что для чего там надобно.
Вот вы подключили библиотеку с функциями, струкурами, дефайнами и др. и почти все их не используете. Будете выпиливать не задействованные и без разницы что компиль это сам может сделать? С секциями тоже самое. Если не используются, они не попадут в прошивку (при адекватных ключах компиля и линкера). А если используются, прошивку не соберете.
Во-первых библиотека на то и библиотека что скрывает от её пользователя всю реализацию, например это стандартная библиотека и она прикомпиливается при сборке и лежит в библах/компилятора, а не в файлах проекта. Когда флеша не хватает, то из библиотеки берётся одна функция, без подключения хедера зависимого от других хедеров. Иногда это экономит и память и флеш.
Мурик писал(а):
Если секции действительно не используются то просто зря потратите время, т. к. они все равно в прошивку не попадут (повторюсь, при правильных ключах компиля и линкера). ... Какая разница сколько файлов в проекте?
Возможно это сложно понять, но мне не хочется видеть лишнего кода при вхождении в новую тему. И чем его меньше (при сохранении читаемости - неассемблер) тем лучше.
Мурик писал(а):
Загружать прошивку как планируете? Обычно прошивку помещают в ОЗУ при отладке (ее загружает отладчик) или необходимости полностью переписать флешь.
Пока не знаю, хотел сначала с эмулятором поиграться, а после уже через uart-ttl через сериал. Говорят в F4 есть встроенный загрузчик он это позволяет.
Мурик писал(а):
То есть строками не пользовались, print в любом виде не использовали, классы динамически не создавали и много чего другого не делали что выделяло память из кучи?
Динамически создавал структуры malloc(), но память быстро кончилась пришлось от этого быстро отказаться ). printf() заменить самодельный uart_print(). Иначе всё в 2 кБ не впихнуть.
Последний раз редактировалось ddr4 Сб авг 27, 2022 18:27:32, всего редактировалось 1 раз.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 26
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения