Форум программистов, компьютерный форум, киберфорум
Assembler, MASM, TASM
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.63/2256: Рейтинг темы: голосов - 2256, средняя оценка - 4.63
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
15.11.2013, 04:52  [ТС]
ГЛАВА 13
ПЕРЕХОДЫ И ЦИКЛЫ
(часть 1/5)
Структура управления – синтаксическая форма в языке программирования, которая служит для предоставления алгоритма управления. Типичными управляющими структурами являются: if ... then ... else; case;goto; for; while ... do; repeat ... until.
Любой язык программирования должен обеспечивать реализацию следующих форм управления процессом выполнения программ:
  1. выполнение последовательности операторов (структура следования);
  2. использование проверки истинности условия для выбора между различными возможными способами действия (структура ветвления);
  3. выполнение определенной последовательности операторов до тех пор, пока некоторое условие истинно (структура повторения). Набор операций, который регулярно повторяется в одной и той же последовательности. При каждом повторении операции могут изменяться. Повторения будут выполняться пока не будет выполнено предписанное условие, например до согласования с элементом данных или до заполнения/исчерпания счетчика.
Структура следования
Простейший способ как-то организовать используемые в программе инструкции состоит в их последовательном размещении одна за другой. К сожалению, вы не сможете написать любую программу как один большой список инструкций. Если компьютеру необходимо принять какое-то решение, ваша программа должна выбрать один из нескольких вариантов инструкций. Программы, в которых присутствует подобная возможность выбора, называются программами с ветвлением. В других ситуациях вам понадобится, чтобы компьютер выполнял один и тот же набор инструкций, поэтому использование цикла (например, FOR NEXT) оказывается намного проще, чем многократное указание одного и того же набора инструкций.
Структура ветвления
Структура ветвления (например IF условие THEN действие) позволяет компьютеру выполнять один из нескольких наборов различных инструкций в зависимости от определенного условия. Структура ветвления предполагает два или больше вариантов набора инструкций, один из которых компьютер должен выполнить в той или иной ситуации.
Структура повторения
Иногда компьютер должен последовательно выполнять одни и те же инструкции. Вместо того чтобы многократно выводить эти инструкции, программист использует цикл. Цикл FOR NEXT выполняется определенное количество раз. Цикл WHILE выполняется до тех пор, пока не прекратит выполняться определенное условие. Таким образом, количество раз, которое выполняется цикл WHILE, изменяется от нуля до бесконечности.
Принятие решений с помощью управляющих операторов
Основное назначение программы – заставить компьютер вести себя определенным образом. Большинство программ должны принимать данные и изменять свое поведение в зависимости от этих данных. Для того чтобы предоставить программе возможность принимать решения, вы должны использовать управляющие операторы.
Когда вы принимаете какое-либо решение, например, что съесть на завтрак, вы задаете себе определенный вопрос: «А хочу ли я яичницу?» Если ответ положительный, вы отправляетесь на кухню или в ресторан.
Компьютеры работают приблизительно также, только люди задают вопросы, а компьютер проверяет булевы выражения. Булево выражение – это нечто, принимающее всего два значения – истина и ложь, нуль и «не нуль». Простейшие булевы выражения сравнивают два значения (две переменные или одну переменную и одно фиксированное значение).
Символы <, >, =, https://www.cyberforum.ru/cgi-bin/latex.cgi?\leq, https://www.cyberforum.ru/cgi-bin/latex.cgi?\geq и https://www.cyberforum.ru/cgi-bin/latex.cgi?\neq называются операторами отношений.
Если вам требуется вывести на печать сообщение только в том случае, если оба булевых выражения правильны.
Вместо того чтобы заставлять компьютер вычислять булевы выражения по одному, вы можете заставить его вычислять сразу несколько булевых выражений, если воспользоваться булевыми операторами. Булев оператор – это не что иное, как объединение двух или нескольких булевых выражений для представления значений true или false.
В любом языке программирования используются следующие булевы операторы: AND, OR, XOR, NOT.
Проще всего объяснить компьютеру какие действия требуется выполнять – использовать оператор IF THEN. Этот оператор проверяет, выполняется определенное условие или нет.
Оператор IF THEN приказывает компьютеру следовать определенным инструкциям только при выполнении какого-то условия. Если условие не выполняется, компьютер просто игнорирует все соответствующие инструкции.
Оператор IF THEN ELSE немного отличается от оператора IF THEN, так как приказывает компьютеру выполнить один набор инструкций в случае выполнения условия, и другой набор – в случае его невыполнения.
Перечислять несколько условий (более трех) при использовании IF THEN ELSE достаточно утомительная задача, поэтому многие языки программирования используют оператор SELECT CASE.
Вообще говоря, программисты стремятся к тому, чтобы компьютер выполнял как можно больше работы, а они – как можно меньше. В идеале, вы должны писать очень небольшие программы не только потому, что их проще отлаживать и при необходимости модифицировать, но и потому, что при их создании вам придется вводить меньше текста.
Один из способов сокращения числа символов, которые должны вводить программисты, состоит в использовании циклов. Основная идея цикла – заставить компьютер повторять выполнение с небольшими модификациями одной или нескольких инструкций. Например, попробуем написать программу, которая выводит на экран числа от 1 до 5.
Code
1
2
3
4
5
print 1
print 2
print 3
print 4
print 5
Если вы хотите, чтобы программа вывела на экран числа от 1 до 5000, представляете, что вам придется сделать? Вам придется набрать пять тысяч почти одинаковых инструкций. Так как вы наверняка не хотите печатать столько инструкций, вы можете использовать циклы для того, чтобы заставить компьютер многократно выполнять определенные инструкции. Компьютер проделает всю рутинную работу за вас и без вашего участия.
Assembler
1
2
3
4
5
 mov i,1
mov ecx,5
a1: print i
inc i
loop a1
Если вы запустите эту программу, то она выполнит то же самое, что и первый вариант программы. Однако этот вариант может напечатать как числа от 1 до 5, так и числа от 1 до 5000000 (достаточно изменить всего одно число в строке mov ecx,5. Циклы заставляют компьютер интенсивно работать и при этом избавляют вас от необходимости вводить лишние команды.
Цикл заставляет компьютер выполнять многократно одни и те же инструкции, однако компьютер должен знать, когда ему остановится. Для того чтобы сообщить компьютеру об этом, вам необходимо указать условие, которое должно представлять значение true или false.
Если операторы в программе выполняются в том же порядке, как они записаны в памяти – это структура следования (рис. 13.1а). Но время от времени этот естественный порядок выполнения команд приходится нарушать, например, на рис. 13.1б нам потребовалось после оператора 1 выполнить оператор 4, минуя операторы 2 и 3. Такую возможность обеспечивает структура ветвления. Если после оператора https://www.cyberforum.ru/cgi-bin/latex.cgi?q нужно выполнить оператор https://www.cyberforum.ru/cgi-bin/latex.cgi?n, который не следует непосредственно за оператором https://www.cyberforum.ru/cgi-bin/latex.cgi?q, то надо иметь возможность сослаться на оператор https://www.cyberforum.ru/cgi-bin/latex.cgi?n в программе. Для этой цели служит метка. Метка – это идентификатор, который может предшествовать любому оператору и отделяется от него двоеточием. Оператор, перед которым стоит метка, называется помеченным оператором. Метка используется в других частях программы для ссылки на этот оператор или для того, чтобы к помеченным строкам программы могли обращаться (по символическим именам) другие части программ, например, в случае передачи управления, или при переходах. Сама метка не меняет последовательности выполнения команд. В языке ассемблера структуру ветвления реализуют командами передачи управления на участок программы, помеченный меткой.
Структура повторения выполняет какую-то часть программы, многократно.
Изменение нормального порядка следования операторов осуществляется специальными операторами или операторными структурами.
Имеется три основных типа операторов, обеспечивающих выполнение, в зависимости от условия (рис. 13.2):
(а) – выполнение при условии: if b then S;
(б) – выбор из двух возможностей: if b then S1 else S2;
(в) – выбор из многих возможностей (оператор выбора).
Заметьте, что (а) – это частный случай (б), то есть:
if b then S else <ничего не делать>
В случае (б) управляющая переменная должна быть логической, а в случае (в) она должна быть численной.
Рис. 13.1
6
а
б
Рис. 13.2
Существует два вида алгоритмов – безусловные и условные. Порядок действий безусловного алгоритма всегда постоянен и не зависит от входных данных. Например: https://www.cyberforum.ru/cgi-bin/latex.cgi?x=y+z. Порядок действий условных алгоритмов зависит от входных данных. Например: если https://www.cyberforum.ru/cgi-bin/latex.cgi?z не равно нулю, то https://www.cyberforum.ru/cgi-bin/latex.cgi?x=y/z, иначе вывести сообщение об ошибке.
Обратите внимание на выделенные ключевые слова если, то и иначе, называемые операторами условия или условными операторами. Без них не обходится ни одна программа. В общем виде оператор условия схематично изображается так:
IF (условие) THEN (оператор1, оператор2) ELSE (оператор3, оператор4)
Задача компилятора – преобразовать эту конструкцию в последовательность машинных команд, выполняющих оператор1, оператор2, если условие истинно и, соответственно – оператор3, оператор4, если оно ложно.
Условия делятся на простые (элементарные) и сложные (составные). Пример простого условия – https://www.cyberforum.ru/cgi-bin/latex.cgi?if (a=b), в то время как пример сложного условия может выглядеть так – https://www.cyberforum.ru/cgi-bin/latex.cgi?if ((a=b) AND (a\neq 0)). Очевидно, что любое сложное условие можно разложить на ряд простых условий.
Существуют три основных типа элементарных условий:
  • условные отношения – меньше, равно, больше, меньше или равно, не равно, больше или равно;
  • логические условия – И (AND), ИЛИ (OR), НЕ (NOT), исключающее ИЛИ (XOR), НЕ ИЛИ (NOR), НЕ И (NAND);
  • проверка битов.
Под ветвлением по условию понимается программная структура, в которой некоторый код исполняется или не исполняется в зависимости от выполнения определенного условия.
Классической условной конструкцией является конструкция if ...then ... endif. В этой конструкции некоторые операторы выполняются только при условии, что содержащееся в конструкции логическое выражение имеет значение TRUE.
При построении алгоритмического языка по Маккарти существенен разбор случаев, поэтому необходимо иметь возможность формально выражать выполнение или невыполнение тех или иных условий. Таким образом, должна быть универсально введена вычислительная структура значений истинности {true, false}. В качестве базовых операций используют обычно две двухместные операции конъюкции и дизъюнкции и одноместную операцию отрицания.
Начнем с очень простого примера. Напишем программу, подсчитывающую количество букв «а» во фразе «Я программирую на ассемблере!»
C
1
2
3
4
5
6
7
main()
{ int count=0, i=0;
char buffer[] = «Я программирую на ассемблере!»;
while (buffer[i] != 0)
if(buffer[i++] == 'а') count++;
printf(«Во фразе %d букв а», count);
}
Оператор if служит указателем компьютеру увеличить значение переменной count на 1, если только что прочитанный символ (содержимое переменной buffer[i]) представляет собой символ «а». Что происходит в случае, когда значение переменной buffer[i] не является символом «а»? Тогда в цикле while происходит чтение следующего символа.
Команды управления, часто называемые командами перехода, позволяют выполнять различные действия в соответствии со значением внешних сигналов или выработанных внутри системы условий. Все команды управления делятся на команды безусловного и условного перехода.
БЕЗУСЛОВНЫЙ ПЕРЕХОД (JMP) – команда, по которой в EIP/IP записывается содержимое адресного поля команды JMP label, то есть обеспечивается переход в программе по адресу label, указанному в команде, при этом следующая команда или группа команд, расположенных после JMP пропускается.
БЕЗУСЛОВНЫЙ ПЕРЕХОД С ВОЗВРАТОМ (CALL переход к подпрограмме, комбинация из PUSH адрес_возврата и JMP адрес_подпрограммы, INT номер_прерывания) – команда, по которой в EIP/IP записывается новое содержимое (адрес первой команды подпрограммы), но в отличие от команды JMP в памяти сохраняется состояние программного счетчика и некоторых других регистров. После выполнения подпрограммы по ее последней команде ВОЗВРАТ (RET) восстанавливается содержимое программного счетчика и всех регистров.
Команды условной передачи проверяют состояние какого-либо разряда регистра, флагового триггера или другого параметра. От результата проверки зависит, будет выполняться переход или нет. Обычно переход выполняется, если результат проверки соответствует указанному в команде условию. В эту подгруппу команд управления входят команды:
  • УСЛОВНЫЙ ПЕРЕХОД (Jcc, JECXZ, LOOP) по адресу; в коде команды Jcc обязательно указывается проверяемое условие, в качестве которого в микропроцессоре используются нулевое или ненулевое значение результата (ZF), положительный или отрицательный знак результата (SF), наличие или отсутствие сигналов переноса (CF), переполнения (OF) и др.; при выполнении условия в программный счетчик записывается содержимое адресного поля команды Jcc label, т. е. обеспечивается переход в программе по адресу label, указанному в команде; при невыполнении условия управление передается следующей команде программы;
  • УСЛОВНЫЙ ПЕРЕХОД С ВОЗВРАТОМ (комбинация PUSH адрес_возврата / Jcc) – переход к подпрограмме происходит только при выполнении указанного в condition code условии;
  • УСЛОВНО ПРОПУСТИТЬ (Jcc, SETcc, CMOVcc, команды SCAS, CMPS префиксом REPE/REPNE с условием, которое не выполняется), по которой проверяется определенное условие, и если оно выполнено, то пропускается следующая команда программы;
  • ПРОПУСТИТЬ ПО ФЛАГУ, используемая в основном для организации взаимодействия микропроцессора и устройства ввода/вывода; по этой команде проверяется состояние флагового триггера устройства ввода/вывода;
  • СРАВНИТЬ И ПРОПУСТИТЬ (комбинация CMP/TEST/SUB/AND и Jcc/SETcc/CMOVcc с условием, которое не выполняется либо LOOP/JECXZ), по которой сравнивается содержимое двух регистров, или регистра и ячейки памяти, или содержимое регистра/ячейки памяти с числом, в случае равенства пропускается следующая команда;
  • ЦИКЛ (LOOP, Jcc), по которой содержимое определенного регистра или ячейки памяти уменьшается (или увеличивается) на единицу, и если оно станет равным нулю, то пропускается следующая команда.
Обычно в систему команд микропроцессора включается еще несколько вспомогательных команд, которые позволяют управлять состоянием регистров или триггеров, влияющих на выполнение условных переходов, например: УСТАНОВИТЬ ФЛАГ (STC, STD, STI); СБРОСИТЬ ФЛАГ (CLC, CLI, CLD); ИНВЕРТИРОВАТЬ ФЛАГ (CMC), УСТАНОВИТЬ КАКОЙ-ЛИБО РАЗРЯД (OR mask); СБРОСИТЬ КАКОЙ-ЛИБО РАЗРЯД (AND mask) и другие.
При выполнении программы регистр IP/EIP/RIP содержит адрес текущей машинной команды. При обработке текущей команды значение IP/EIP/RIP автоматически увеличивается на длину команды, и это новое значение становится адресом следующей исполняемой команды. В результате, команды обрабатываются по последовательно
возрастающим адресам. Переход от одной последовательности команд к другой выполняется командами передачи управления (jmp, call, ret, jcc, loop), которые принудительно устанавливают значение IP/EIP/RIP.
Переходы бывают условными и безусловными. Если переход делается только тогда, когда выполнено некоторое условие, то такой переход называется условным, а если он выполняется независимо от каких-либо условий – безусловный.
Команда безусловного перехода JMP(Переход, прыжок =”JUMP”)
Компьютер использует несколько команд безусловного перехода, но в языке ассемблера они обозначаются одинаково. Команда JMP передает управление в другую точку программы, не сохраняя какой-либо информации для возврата.
Синтаксис команды:
JMP <TAGER>
Семантика команды: операнд TAGER тем или другим способом указывает адрес перехода, то есть адрес той команды, которая должна быть выполнена следующей. Если команда, на которую делается переход находится в том же сегменте памяти, что и команда jmp – переход называется внутрисегментным или ближним (near jmp) – изменяется только значение в регистре IP/EIP/RIP, если при этом адрес перехода находится в пределах от -128 до +127 байтов от команды jmp – такой переход называется коротким (short jmp). Если команда находится в каком-либо другом сегменте программы – такой переход называется межсегментным или дальним (far jmp) – изменяется значение в регистре IP/EIP /RIP, а также значение CS. Для понимания различий механизмов перехода в реальном и защищенном режимах нужно помнить следующее. В реальном режиме микропроцессор изменяет CS и IP/EIP/RIP в системной памяти в соответствии с положением команды, на которую делается переход. В защищенном режиме микропроцессор предварительно анализирует байт прав доступа AR в дескрипторе, номер которого определяется по содержимому сегментной части указателя. В зависимости от состояния байта AR микропроцессор по команде JMP выполняет либо переход, либо переключение задач – передачу управления другой задаче в многозадачной среде.
Прямой переход
Синтаксис команды: JMP <TAGER>
Возможные варианты команды:
jmp rel
jmp ptr16:16/32/64

Семантика команды: в случае прямого перехода в качестве операнда TAGER указывается адрес перехода, то есть адрес той команды, на которую надо передать управление. Адрес перехода может быть указан в виде:
  • rel (relative address относительный адрес) – относительного смещения от конца инструкции. Смещение, в зависимости от установленного режима (use16/32/64), может быть 8-, 16-, 32- или 64-битным. Смещение может быть положительным, то есть направленным «вниз» от текущей команды (в сторону роста адресов), или отрицательным, то есть направленным «вверх» (в сторону уменьшения адресов). Величину смещения автоматически рассчитывает ассемблер, программисту достаточно указать метку той инструкции, на которую должен произойти переход.
  • ptr16:16/32/64 – абсолютного адреса (в виде сегмент:смещение) системной памяти.
Псевдокод:
IF близкий переход
IF 64-разрядный режим
THEN
IF короткий переход
THEN
RIPhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrowRIP+Знаковое_расширение(TARGET)
;в RIP адрес инструкции, следовавшей за JMP
IF близкий прямой переход
THEN
RIPhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrowRIP+TARGET;в RIP адрес инструкции, следовавшей за JMP
ELSE ;близкий косвенный переход
RIPhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow TARGET
ENDIF
ELSE IF 32-разрядный режим
IF короткий переход
THEN
EIPhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrowEIP+Знаковое_расширение(TARGET)
IF близкий прямой переход
THEN
EIPhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrowEIP+TARGET;в EIP адрес инструкции, следовавшей за JMP
ELSE ;близкий косвенный переход
EIPhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow TARGET
ENDIF
ENDIF
Assembler
1
2
3
 JMP L;следующей будет выполняться команда MOV EAX,0
MOV EBX,0
L:MOV EAX,0
Адрес перехода Смещение
[EIP]–12880h
...
[EIP]–20FEh
[EIP]–10FFh
[EIP]0
[EIP]+11
[EIP]+22
...
[EIP]+1277Fh
Использование опкода EB (JMP SHORT)
Нам необходимо перейти к метке A1. Как это сделать? Рассчитываем смещение от команды JMP до метки A1.
- 4000C4https://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow адрес метки A1
4000C9https://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow значение в регистре EIP на момент выполнения JMP
-5=0FBh https://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow смещение
Число 0FBh получает знаковое расширение и складывается со значением в регистре IP/EIP/RIP.
0FBhhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow FFFFFFFB
[EIP] = 004000C9
 004000C4 https://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow адрес, на который мы перейдем
Но такие переходы возможны лишь в пределах от -128 до +127 байт, а что делать если переход должен быть дальше?
Использование опкода E9 (JMP NEAR)
В команде JMP NEAR длина смещения, которая будет складываться со значением в регистре IP/EIP/RIP – четыре байта для 32-битного, либо два байта для 16-битного режима.
адрес  код
4000C4:A1: INC ECX 
4000C5: ADD EAX,[ECX]0303
4000C7: JMP NEAR A1E9FFFFFFF8
4000C9:https://www.cyberforum.ru/cgi-bin/latex.cgi?\leftarrow значение в EIP
Команда JMP NEAR дает возможность делать переходы от -2147483648 (8000000h) до +2147483647 (7FFFFFFFh) байт для 32-битного, либо -32768 (8000h) до +32767 (7FFFh) байт для 16-битного режима.
При сложении IP/EIP со смещением, перенос из старшего разряда IP/EIP игнорируется, поэтому адрес IP/EIP вычисляется по кольцевому принципу в пределах текущего сегмента кода, то есть:
0FFFFFFFFh+0FFFFFFFFh=0FFFFFFFEh
Для перехода на б?льшие расстояния используют команду JMP FAR.
Использование опкода EA (JMP FAR)
Существует третий способ перехода с указанием не относительного, а действительного адреса. Для этого нам требуется не только знание EIP, на который мы должны перейти, а еще и значение соответствующего CS.
адрес  код
1234:004000C4:A1: INC ECX41
1234:004000C5: ADD EAX,[ECX]0303
1234:004000C7: JMP FAR PTR 1234:4000C4hEAC40040003412
По команде JMP FAR будет изменено не только значение, содержащееся в регистре IP/EIP/RIP, но и значение в регистре CS. Команда JMP FAR позволяет передать управление в любую точку любого сегмента. Транслятору необходимо сообщить, что переход дальний с помощью описателя far ptr, указываемого перед именем точки перехода.

7
Закрытая тема Создать тему
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru