Для чего нужны сегментные регистры перечислите их
Перейти к содержимому

Для чего нужны сегментные регистры перечислите их

  • автор:

5. Сегментные регистры

В программной модели микропроцессора имеется шесть сегментных регистров: cs, ss, ds, es, gs, fs.

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

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

Микропроцессор поддерживает следующие типы сегментов.

1. Сегмент кода. Содержит команды программы. Для доступа к этому сегменту служит регистр cs (code segment register) – сегментный регистр кода. Он содержит адрес сегмента с машинными командами, к которому имеет доступ микропроцессор (т. е. эти команды загружаются в конвейер микропроцессора).

2. Сегмент данных. Содержит обрабатываемые программой данные. Для доступа к этому сегменту служит регистр ds (data segment register) – сегментный регистр данных, который хранит адрес сегмента данных текущей программы.

3. Сегмент стека. Этот сегмент представляет собой область памяти, называемую стеком. Работу со стеком микропроцессор организует по следующему принципу: последний записанный в эту область элемент выбирается первым. Для доступа к этому сегменту служит регистр ss (stack segment register) – сегментный регистр стека, содержащий адрес сегмента стека.

4. Дополнительный сегмент данных. Не явно алгоритмы выполнения большинства машинных команд предполагают, что обрабатываемые ими данные расположены в сегменте данных, адрес которого находится в сегментном регистре ds. Если программе недостаточно одного сегмента данных, то она имеет возможность использовать еще три дополнительных сегмента данных. Но в отличие от основного сегмента данных, адрес которого содержится в сегментном регистре ds, при использовании дополнительных сегментов данных их адреса требуется указывать явно с помощью специальных префиксов переопределения сегментов в команде. Адреса дополнительных сегментов данных должны содержаться в регистрах es, gs,fs (extension data segment registers).

6. Регистры состояния и управления

В микропроцессор включены несколько регистров, которые постоянно содержат информацию о состоянии как самого микропроцессора, так и программы, команды которой в данный момент загружены на конвейер. К этим регистрам относятся:

1) регистр флагов eflags/flags;

2) регистр указателя команды eip/ip.

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

1. eflags/flags (flag register) – регистр флагов. Разрядность eflags/flags – 32/16 бит. Отдельные биты данного регистра имеют определенное функциональное назначение и называются флагами. Младшая часть этого регистра полностью аналогична регистру flags для 18086. На рисунке 3 показано содержимое регистра eflags.

Рис. 3. Содержимое регистра eflags

Исходя из особенностей использования флаги регистра eflags/flags можно разделить на три группы:

1) восемь флагов состояния.

Эти флаги могут изменяться после выполнения машинных команд. Флаги состояния регистра eflags отражают особенности результата исполнения арифметических или логических операций. Это дает возможность анализировать состояние вычислительного процесса и реагировать на него с помощью команд условных переходов и вызовов подпрограмм. В таблице 1 приведены флаги состояния и указано их назначение.

2) один флаг управления.

Обозначается df (Directory Flag). Он находится в 10-м бите регистра eflags и используется цепочечными командами. Значение флага df определяет направление поэлементной обработки в этих операциях: от начала строки к концу (df = 0) либо наоборот, от конца строки к ее началу (df = 1). Для работы с флагом df существуют специальные команды: eld (снять флаг df) и std (установить флаг df). Применение этих команд позволяет привести флаг df в соответствие с алгоритмом и обеспечить автоматическое увеличение или уменьшение счетчиков при выполнении операций со строками.

3) пять системных флагов.

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

Таблица 1. Флаги состояния

Таблица 2. Системные флаги

2. eip/ip (Instraction Pointer register) – регистр-указатель команд. Регистр eip/ip имеет разрядность 32/16 бит и содержит смещение следующей подлежащей выполнению команды относительно содержимого сегментного регистра cs в текущем сегменте команд. Этот регистр непосредственно недоступен программисту, но загрузка и изменение его значения производятся различными командами управления, к которым относятся команды условных и безусловных переходов, вызова процедур и возврата из процедур. Возникновение прерываний также приводит к модификации регистра eip/ip.

Сегментные регистры

В реальном режиме процессора при записи адреса ячейки в памяти используется формат СЕГМЕНТ:СМЕЩЕНИЕ, где СЕГМЕНТ — это номер сегмента (блока памяти), а СМЕЩЕНИЕ — это номер ячейки памяти относительно начала сегмента. Почему так повелось, я рассказал здесь. А в этой статье расскажу о сегментации памяти и сегментных регистрах.

Сегментация памяти

В реальном режиме используется сегментная модель памяти. Суть её заключается в том, что вся память разделена на блоки по 64 КБ. Эти блоки и называются сегментами. У каждого сегмента есть свой адрес. Например, нулевой сегмент имеет адрес 0000. В пределах каждого сегмента каждая ячейка памяти (а в одной ячейке памяти может храниться один байт) также пронумерована, но уже относительно сегмента. Например:

0000:0000 - ячейка 0 в сегменте 0 0000:0001 - ячейка 1 в сегменте 0 0001:0000 - ячейка 0 в сегменте 1 0001:0015 - ячейка 21 в сегменте 1

Обратите внимание на последний пример. Почему ячейка номер 21? Да потому что все числа в такой модели памяти представлены в шестнадцатеричной системе. То есть 15 в шестнадцатеричной — это 21 в десятичной.

Допустим, что у нас в разных сегментах записаны данные. Например, буквы:

0010:Привет, 0020:МИР!

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

0010:0000 (П) 0010:0001 (р) 0010:0002 (и) 0010:0003 (в) 0010:0004 (е) 0010:0005 (т) 0010:0006 (,) 0020:0000 (М) 0020:0001 (И) 0020:0002 (Р) 0020:0003 (!)

Конечно, в реальной программе никто не будет размещать одну строку в разных сегментах. Но это просто пример. Здесь мы знаем адрес каждого символа нашей строки. Мы можем прочитать из памяти отдельный символ, или записать по данному адресу другой символ, изменив таким образом строку. Например, если мы запишем в адрес 0020:0002 символ Г , то в памяти у нас будет:

0010:Привет, 0020:МИГ!

Эта ячейка памяти смещена от начала сегмента на три байта. Но, поскольку нумерация начинается с 0 , то мы записываем букву в байт номер 2 от начала смещения.

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

Сегментные регистры процессора

Для хранения адресов сегментов используются сегментные регистры: CS, DS, SS, ES. Эти регистры используются для обращения к сегментам данных. Смещения могут храниться в других регистрах (но не в любых).

  • Регистр CS (Code Segment — сегмент кода) используется для хранения сегмента кода программы. То есть в этом сегменте хранится код программы.
  • Регистр DS (Data Segment — сегмент данных) используется для хранения данных программы.
  • Регистр SS (Stack Segment — сегмент стека) используется для хранения сегмента стека.
  • Регистр ES — дополнительный регистр, который может хранить адрес любого сегмента (например, видеобуфера). Именно этот регистр наиболее часто используется в программах для работы с памятью.

Для примера попробуем записать в регистр ES адрес сегмента 0020:

MOV ES, 0020h

Простое, казалось бы, решение. Но оно не работает! А всё почему? А потому что запись любого числа напрямую в сегментные регистры запрещена! Поэтому вместо одной команды приходится выполнять две:

MOV AX, 0020h MOV ES, AX

то есть сначала записываем сегмент в регистр АХ, а только потом можно записать содержимое АХ в сегментный регистр.

На этом пока всё. Подключайтесь к группе Основы программирования в Телеграм, или к другим каналам (ссылки ниже), чтобы ничего не пропустить.

Сегментные регистры

Сегментные регистры используются при формировании линейных адресов памяти. Вторым компонентом для формирования линейного адреса является эффективным адресом (применительно к архитектуре IA-32 эти называния равноправны, хотя первое применяется намного чаще). Способ вычисления линейного адреса зависит от режима работы процессора и подробно описан в разделе Формирование линейного адреса .

В микропроцессоре 8086 было четыре 16-разрядных сегментных регистра:

  • CS — сегментный регистр кода;
  • DS — сегментный регистр данных;
  • ES — сегментный регистр дополнительных данных;
  • SS — сегментный регистр стека.

Начиная с микропроцессора 80386, сегментных регистров стало шесть, но их разрядность не изменилась. «Новичками» стали два сегментных регистра дополнительных данных — FS и GS.

Общие правила использования сегментных регистров процессором таковы:

SegmentRegister

  • для выборки кода команды всегда используется сегментный регистр CS;
  • при обращении к стеку ( реальном режиме содержимое каждого сегментного регистра представляет собой номер защищённом режиме каждый сегментный регистр делится на три части, как показано на рисунке: Бит TI в этом случае указывает, какая таблица дескрипторов должна использоваться: нуль соответствует таблице глобальных дескрипторов (GDT), единица — таблице локальных дескрипторов (LDT). Поле Index является номером (индексом) дескриптора в таблице дескрипторов; этот дескриптор используется при вычислении линейного адреса. Наконец, поле RPL является 64-разрядном режиме сегментные регистры CS, DS, ES и SS в формировании линейного адреса не участвуют, поскольку сегментация в этом режиме не поддерживаются. Сегментные регистры FS и GS могут использоваться в качестве дополнительных регистров базы, о чём подробнее говорится в разделе Эффективный адрес.

    Дневники чайника. Чтива 0, виток0

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

    Большая часть кода программ состоит из команд копирования значений из оперативной памяти в регистры и обратно.

    Знаю по себе: понять, зачем нужны регистры можно только через практику. Другого способа нет.

    Причём где-то дней 10-15 я вообще не врубался — как оно всё. чтобы вот как-то вот так. 🙂

    Но сейчас мне кажется, что регистры — самое простое, и в то же время важное, что есть в процессоре.

    • Приём данных от пользователя (данные передаются в регистры).
    • Вычисления (загрузили в регистр число и дали команду, как его обработать).
    • Приём данных из устройств (опять они — регистры).
    • Изменение состояния ЦП и периферийных устройств (только регистры! Например, регистр флагов).
    • Вывод данных на устройства.
    • Передача данных другим программам.

    Во всех операциях программы (а я перечислил только основные) без регистров не обойтись.

    И если даже данные не хранятся в регистрах, то в них обязательно будут указатели на эти данные (адреса данных в памяти), других способов работы с данными у процессора нет, только через собственные регистры. А если речь идёт исключительно о сторонних устройствах (например, звуковуха, видяха, винт и т.д.), то мы тоже используем регистры, только это уже не регистры процессора, а, например, PCI-регистры, или AGP, или SATA и так далее.

    Так что тема АРХИВАЖНАЯ! Но непонятная без примеров. Их должно быть как минимум 10-15 (по одному в день).

    Что-то я разболтался, давайте уже к делу.

    Внешний вид отладчика Code View (png 19,021b).

    Загрузите prax01.com в отладчик CodeView. Для этого скопируйте файл в каталог отладчика (примечание) и запустите из командной строки «cv prax01.com». Отладчик CodeView у вас должен выглядеть примерно так:

    • В окне 3 — дизассемблированный код.
    • В окне 5 — hex-байты. Их адреса (слева) и символы, которые они означают (справа).
    • В окне 7 — регистры процессора.
    • 9 — командная строка.

    Итак, смотрим на код программы в памяти:

    [3] Адреса Байты Имена Операнды 12BD:0100 B409 MOV AH,09

    12BD — сегмент нашей программы, он может быть и другим.

    Поскольку все примеры для DOS у нас значительно меньше 64Kb, они будут в одном сегменте.

    Значит, как я уже говорил, до следующего витка мы можем игнорировать сегменты вообще, а уж в Win32 тем более.

    У com-программ в оперативной памяти адрес первой машинной команды равен 100h.

    Так происходит потому, что ДОСу первые адреса нужны для служебной информации о программе: командная строка, атрибуты и всё такое (можете посмотреть в окне[5], что там).

    ДОС-программы нас уже не очень интересуют, и об этом больше говорить не будем.

    А в Win-программах происходит примерно то же самое, только адрес первой машинной команды обычно находится чуть дальше. В самых простых приложениях — 00401000h. 🙂

    Нужно просто прибавлять 100h, чтоб узнать адрес в памяти из адреса в com-файле. Так же, как вы складываете 8+100d=108d. Будет всё точно так же, 8+100h=108h.

    Я легко посчитал адрес текстовой строки в оперативной памяти (010D) и заложил его в код программы.

    Ведь мы знаем номер байта в файле, где начинается строка текста. 0D+100h — и вот он, искомый адрес в памяти.

    Главная кнопка здесь будет F10, вам нужно нажимать только на неё (всё остальное можно делать мышкой). Эта кнопка с каждым нажатием будет выполнять текущую строку.

    Вы уже понимаете, что отображается в окне[5], и также имеете представление, что в окне[3] — код программ. А вот окно [7] для вас пока ничего не означает, и это очень досадно, но мы сейчас исправимся.

    Нажмите F10 один раз. Выполнится строка

    mov ah,09

    Эта команда означает: поместить значение 09 в регистр AH. Но в окне[7] нет регистра AH, а после выполнения этой строки изменится регистр EAX.

    Было EAX=00000000 Стало EAX=00000900

    Дело в том, что регистр AH — это часть регистра AX, а он — часть регистра EAX.

    Если, предположим, мы загрузили регистр EAX значением 44332211, выглядеть он будет так:

    EAX=44332211 AX= 2211 AH= 22 AL= 11

    Один 32-разрядный регистр — это всего лишь 4 байта в процессоре.

    Точно так же устроены ещё 3 регистра.

    Схематично их отображают так:

    EAX (сокращение от Accumulator) EBX (сокращение от Base) ECX (сокращение от Counter) EDX (сокращение от Data)

    Можно, кстати, писать и строчными буквами, регистры не обидятся.

    EAX, EBX, ECX и EDX — 32-битные регистры данных центрального процессора (от 386-го и по сей день). В эти регистры помещаются необходимые значения, и в них же чаще всего оказываются результаты вычислений. В 32 битах можно хранить hex-число от 00 00 00 00h до FF FF FF FFh. И это всё, что может там храниться. На назначения (Accumulator, Base, Counter, Data) в наших уроках можно вообще не обращать внимания. Когда вы освоите команды Ассемблера, вы сами поймете, почему Counter и почему Base.

    Первая строка кода из нашей программы уже почти понятна.

    mov ah,9
    Имя команды операнд1,операнд2 где операнд1 - регистр AH операнд2 - цифра 9

    Остаётся непонятной только сама команда.

    MOV — это основная команда Ассемблера, она встречается в программах гораздо чаще остальных.

    Для программирования самое важное понятие — переменная. На самом деле переменная лишь абстракция для удобства программирования. Такая же абстракция, как в алгебре X, Y, Z. Чтоб решить какую-нибудь задачу, мы говорим, что Y будет равен 5, а Z будет равен 3.

    Так вот присвоение переменной Y значения 5 на Ассемблере будет выглядеть так:

    mov Y,5

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

    Этого достаточно, чтоб использовать команду в своих программах. Посмотрите ещё раз на первые две строки prax01:

    Имена Операнды комментарии команд mov ah,09 ; поместить значение 9 в регистр AH mov dx,010D ; поместить значение 010Dh в DX

    Получив первую инструкцию, процессор выполнит инициализацию своего 8-битного регистра AH значением 9, после чего регистр AH будет содержать только байт 09.

    При выполнении второй команды процессор поместит в свой 16-битный регистр DX значение 010Dh, после чего регистр DX будет содержать только эти два байта 01 и 0Dh.

    Причём, если вы ещё раз посмотрите на устройство регистров, вы обязательно поймёте следующее:
    Так как регистр DX состоит из DH и DL, то можно сказать, что после выполнения второй строки кода программы в регистре DH окажется значение 01, а в регистре DL окажется значение 0Dh.

    DH DL DX=01 0D

    Это просто, но важно! В 16-битный регистр (AX,BX,CX,DX) нельзя положить значение больше двух байт (FFFFh).

    А в 8-битный (AH,AL, BH,BL, CH,CL, DH,DL) нельзя положить больше байта, то есть FFh.

    EAX=99884433 AX= 4433 AH= 44 AL= 33

    Вы должны понять, что физически есть только 4 байта (99 88 44 33h). По отдельности можно обращаться к AX за значением 4433h, или к AH за 44h, или к AL за 33h. Но 9988h находится в E-части, а у неё нет собственного имени, она не является подрегистром. Вы не можете прочитать или загрузить 2 старших байта такого регистра, не обратившись ко всему регистру. Пример:

    mov EAX, 0FFFFFFFFh ; Так правильно, и EAX будет равен FFFFFFFF mov EAX, 01FFFFFFFFh ; Так НЕправильно. Значение больше регистра, ; данной операции быть не может mov EAX, 0 ; Так правильно, и EAX станет равен 00000000 mov AX, 0FFFFh ; Так правильно, и EAX будет равен 0000FFFF mov AX, 1FFFFh ; Так НЕправильно. Значение больше регистра, ; данной операции быть не может mov AX, 0 ; Так правильно, и AX станет равен 0000 mov AH, 111h ; Так НЕправильно. Значение больше регистра, ; данной операции быть не может mov AL, 100h ; Так НЕправильно. Значение больше регистра, ; данной операции быть не может mov AL, 0BBh ; Так правильно, и EAX станет равен 000000BB mov AH, AL ; так правильно, и EAX станет равен 0000BBBB

    Это, думаю, понятно. А к старшей части EAX отдельно обращаться можно, например, при помощи команд сдвига битов:

    shl EAX,10h ; Сделает теперь регистр EAX равным BBBB0000 shr EAX,10h ; А эта команда сделает его обратно равным 0000BBBB

    О командах сдвига потом напишу подробно, сейчас я их привёл, только чтоб ответить на вопрос, как можно отдельно читать/записывать 2 старших байта E-регистров.

    Итак, мы «познакомились» с четырьмя регистрами общего назначения (РОН), и есть ещё четыре.

    Регистры-указатели

    Они тоже входят в группу РОН. В этой программе нам они безразличны, но далее мы научимся работать и с ними. Причём на практике всё будет очень похоже на регистры данных. Да и вообще отличий между ними совсем немного. Главное отличие в том, что в регистрах-указателях нет подрегистров, есть только одна вложенная часть.

    ESP=44332211 (Extended Stack Pointer) SP= 2211 (Stack Pointer)

    Мы опять видим четыре байта, ведь все E-регистры 32-разрядные (32 бита — это 4 байта).

    EBP включает в себя BP (Base Pointer) указатель базы ESP включает в себя SP (Stack Pointer) указатель стека ESI включает в себя SI (Source Index) индекс источника EDI включает в себя DI (Deliver Index) индекс приёмника

    Эти страшные слова «указатель базы», «индекс» на самом деле для нас практически ничего не означают. Вы можете забыть о назначении этих регистров и совершенно свободно использовать регистры ESP, EBP, ESI, EDI, как вам захочется. Только нужно сохранять их содержимое на время использования и восстанавливать обратно. Поэтому сейчас не забивайте голову индексами, базами и стеками. Хотя как раз про стек мы скоро поговорим.

    Я уже рассказал почти обо всех регистрах, с которыми нам придётся иметь дело. Остались 2 специальных регистра: EIP и E-flags.

    Регистр адреса текущей машинной команды — EIP

    Процессор берёт из памяти машинную команду и увеличивает текущий адрес так, чтобы он указывал на следующую команду. Именно для этого и существует EIP.

    EIP=44332211 (Extended Instruction Pointer) IP= 2211 (Instruction Pointer)

    И опять посмотрите на первые две строки кода в отладчике:

    Адрес Байты Имена Операнды 0100: B409 mov ah,009 0102: BA0D01 mov dx,0010D

    Обратите внимание на столбик с байтами. Первая инструкция занимает в памяти два байта (B4h — байт кода операции, 09 — значение в команде). А вторая уже 3 байта, так как значение больше. Есть инструкции, которые занимают аж 15d байт.

    Упростив процес выполнения машинной команды, можно сказать так: процессор знает, что если в первом байте содержится код операции 1011 0. b (от B0h до B7h), то это означает, что машинная команда занимает два байта. Тогда он увеличивает значение регистра EIP на два. Затем, выполнив саму маш.команду, он берёт следующий байт по адресу, указанному регистром EIP, и, узнав, что там инструкция с опкодом 1011 1. b (от B8h до BFh), увеличивает значение EIP на три. И так далее.

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

    Есть такие команды Ассемблера, цель которых просто изменить регистр EIP, например, jmp (от слова jump — прыгать). Допустим, ЦП получает машинную команду EB 10h, тогда он выполняет изменение EIP и сразу же приступает к выполнению следующей инструкции — той, что теперь будет указана в EIP.

    Теоретически все это запомнить очень сложно и, самое главное, не нужно. Потому что на практике всё станет просто как 2х2.

    Мы ещё не обсудили регистр Eflags, который для нас будет очень важен. Говорить о нём без примера программы, мне кажется, глупо. Скоро и до него доберёмся.

    Также вы очень часто будете натыкаться на сегментные регистры (CS,DS,ES,FS,GS,SS). Они фигурируют во всех справочниках, отображаются в отладчиках и даже указываются в командах (например: mov byte ptr DS:[EBX],01).

    Но сегментные регистры нужно обсуждать вместе с сегментацией памяти, а это отдельная тема, не первой важности.

    На сегодняшнем этапе развития процессоров и операционных систем понимание сегментации можно отложить до следующего витка.

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

    Проведите prax01.com по отладчику CodeView самостоятельно (клавишей F10). Повторите этот процесс несколько раз, посмотрите, как будут меняться регистры. Найдите в окне памяти[5] код и данные программы. Чем больше времени вы будете проводить в отладчике, тем быстрее вы станете толковым программистом.

    Я очень хорошо помню своё обучение. Самые сложные дни уже позади, ещё пару дней будет трудно и непонятно.

    Осталось совсем немного корпеть над учебниками, скоро будут сплошные приключения, держись, юнга.

    Вернуться на главную

    — CodeView DOS’овый отладчик, он не любит длинных, русских или сложных путей, поэтому рекомендую держать его папку в корне диска.

    Форточки будут тормозить, пока программа в отладчике. И другие вы загрузить не сможете. Похожие фокусы есть во всех дебагерах — такой уж класс программ.

    Кроме того, ДОС-программы в хрюшке сами по себе здорово тормозят машину. Но для нас это не критично. Я надеюсь 🙂

    Чтоб в CodeView всё выглядело именно так, нужно включить в «Options» пункт «32-bit registers». И для правильного отображения нужно выключить «Screen Swap» (если брали CV у меня — всё уже настроено).

    — Регистр EIP указывает на следующую инструкцию, которая будет выполнена, только с точки зрения внешней архитектуры процессора. Если говорить о процессоре изнутри, то изменение EIP и выполнение действия нескольких инструкций может происходить параллельно (в разных блоках).

    Опять же земля не плоская, но для нас в отладчике — это так. 🙂

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *