Калашников.ru - Ассемблер? Это просто!.. (Выпуск № 014)

InterReklama Advertising

Доброе время суток, друзья мои!

Выпуск N 014

Какой чудесный день!

Какой крутой мой "Пень"!

Какой веселый я:

Рассылочка пришла!

Чего у нас сегодня-то?

Заметки, вопросы, новости

Создается клуб экспертов !!!

Ваши письма

Оболочка Super Shell!

Заметки, вопросы, новости

Ох, друзья мои! Ну и болел же я! Целую неделю. К сожалению, так и не смог выпустить этот номер рассылки вовремя. В связи с этим прошу прощения за такую длинную задержку. Хотелось бы и вас предупредить: будьте осторожны! Не болейте!

Дорогие мои подписчики! Я печатаю рассылку для того, чтобы вы научились писать программы на Ассемблере. Более того, я очень хочу привить у вас любовь к самому языку и показать то, что он не такой уж сложный. Я очень рад тому, что многие используют информацию с рассылки для написания контрольных и/или лабораторных работ. Я просто счастлив, что смог кому-то помочь!

Однако, я был бы очень разочарован, узнав, что информация из рассылки "Ассемблер? Это просто! Учимся программировать" используется кем-то в коммерческих целях. Прошу вас: уважайте, пожалуйста, чужой труд! Если у вас есть предложение по использованию информации в рассылке не только для собственного, частного использования, но и в коммерческих целях (сюда НЕ входит написание собственных рефератов, контрольных и лабораторных работ и т.п.), то, будьте так добры: напишите мне ваше предложение! Мы найдем общий язык!

В противном случае, мне придется поступать по всей строгости Закона РФ "Об авторских и смежных правах".

____________________

Ко мне пришло много писем с просьбой включить e-mail в базу данных с тем, чтобы автоматически высылать вам файлы-приложения к рассылке (sshell.asm, virus.asm, resid.asm). Я отправляю их порциями по 10-20 адресам. Верю, что все подписчики порядочные и солидные люди. Однако, если кто-либо получит некоторое подобие спама, используя адреса, перечисленные в поле "Кому:", сообщите мне об этом. Будем таких искать и безбожно бить ногами...

___________________

Почему мы пишем только *.com файлы? Да потому, что модель памяти в Windows очень напоминает модель памяти *.com-файлов, которые мы сейчас рассматриваем. Конечно, мы напишем несколько примеров *.exe-файлов, но это будет попозже.

К слову. Модель памяти в *.com-файлах называется TINY (крошечная). В Windows - FLAT (плоская или как это?).

____________________

Хочу спросить у образованных людей: есть ли в русском языке слово "запомненный"? Если нет, то каким словом его можно заменить?

Пример: "Достаем со стека запомненный адрес строки".

Спасибо!

____________________

Совсем недавно я поместил на сайт свой небольшой фотоальбом, в котором можно посмотреть фотографии моей жены, сына, брата, сестры, отца, матери, а также меня лично. Если Вам интересно, то можете заглянуть в свободное время...

Создается клуб экспертов

Итак, время пришло! Некоторые уже ознакомились с условиями на сайте, но я все же рекомендую прочитать то, что находится в данном разделе рассылки, причем всем подписчикам.

Не все имеют прямой и постоянный выход в Сеть. В связи с этим я постараюсь вкратце объяснить суть нашего так сказать эксперимента.

Почему я решил сделать это?

Ко мне приходит много писем с различными вопросами (как по Ассемблеру, так и по другим темам; например, по работе с DOS). Я, как уже писал, не успеваю отвечать на все ваши письма. Поэтому я решил провести небольшой эксперимент: создать т.н. "Клуб экспертов".

Что из себя представляет этот (как его?) "Клуб экспертов"?

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

Вася не умеет работать с DOS (не знает, как запустить программу, распаковать файл, как пользоваться MASM / TASM и пр.);

У Пети возник вопрос по поводу работы дизассемблера;

Саша написал свою программу, используя знания полученные из рассылки, но - вот беда! - она не работает так, как Саша хотел бы. Что делать?

Вова вообще только что подписался и не знает что да как. Куда и к кому обратиться?

Дима не понял ничего в стеке. У него возник вопрос. Кто сможет помочь нашему подписчику?

и пр.

Зато у Сергея есть время, определенные знания и желание отвечать на вопросы, помогать другим освоить Ассемблер, но нет возможности. Более того, Сергей знает, что при объяснении темы или при ответе на вопрос сам чего-то да начинает понимать. Т.е. будет учиться сам, обучая других.

Я хочу предоставить эту возможность Сергею и всем остальным, кто имеет то же желание.

Каков принцип работы клуба?

Например, у меня зарегистрировалось 5 специалистов-экспертов, которые изъявили желание отвечать на вопросы связанные с DOS. Вася (который не может разобраться с DOS) высылает мне письмо с вопросом, которое автоматически направляется всем пятерым экспертам. Эксперты, в свою очередь, отвечают на вопрос и отправляют его напрямую Васе. Т.о. Вася получит 5 писем с ответами на свой вопрос. Из одного письма он почерпнет одно, из другого - второе и т.д. При этом эксперт направляет мне копию данного письма. Нет, не для того, чтобы я проверял, а для того, чтобы я мог создавать потихоньку базу данных вопросов/ответов, которую потом выложу на сайт. При этом, естественно, письмо будет сохранено в первозданном виде, причем имя и e-mail эксперта остается (если он этого захочет).

Что я могу предложить экспертам?

Вася, получивший 5 писем с ответами направляет мне оценки всем пяти экспертам по пятибалльной шкале (неужели Васе будет это сложно сделать?). Т.е. Валик ответил на "отлично", а Толик на "троечку" еле тянет. Я периодически опубликовываю в рассылке имена экспертов и количество набранных ими баллов.

Тройка лидеров получает возможность:

опубликовать в рассылке любую информацию о себе;

указать в рассылке адрес своего сайта;

разместить какую-нибудь рекламу;

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

Почему я написал "может"? Потому что какое-то время (пока система не заработает в определенном ритме, если она вообще заработает!) денежки выплачиваться не будут. Зато потом можно будет заработать не только на пивко, но еще и на воблочку. А там, глядишь, и до икорки дойдет... По крайней мере, я очень на это надеюсь...

Но не все так просто. Все эксперты должны соблюдать определенные правила, которые будут высланы им в скором времени. Количество экспертов ограничивается пока 10 человеками на одну тему.

Что требуется от подписчиков, задающих вопросы?

Прежде всего - порядочность и внимательность. А именно:

обязательно отправлять мне справедливо поставленную оценку за ответ (если Вам помог эксперт в том или ином вопросе, навел Вас на Мысль, то почему бы Вам не помочь ему и наоборот?). Момент! Эксперты не будут знать какую оценку дал подписчик на его ответ!;

четко излагать вопрос, передавая эксперту как можно больше информации ("У меня не работает компьютер. Что делать?" - не годится! В лучшем случае эксперт ответит Вам: "Попробуйте воткнуть вилку в розетку!", на что Вы посчитаете его идиотом и отправите мне оценку за ответ "кол". А это несправедливо!);

не задавать вопросы, например, эксперту по оболочкам DOS, касающиеся, скажем, работы с отладчиком (эксперт, которому пришел вопрос "не по адресу", на него не отвечает!);

обязательно указывать в теле письма свой адрес (а иначе как ответит Вам эксперт? Письмо-то я фактически буду отправлять!);

не использовать в корыстных или иных подобных целях адреса экспертов (найду - не знаю, что сделаю!);

отправлять мне письмо, если никакого ответа не последует от экспертов вообще (что я сомневаюсь, но все-таки!);

в любых спорных случаях присылать мне письмо (жалобу), цитируя при этом переписку с экспертом.

Все вышеперечисленное - самое главное! Для этого я и хочу сперва пропускать письма через свой ящик.

_______

Ниже приведена перечень тем для вопросов, который будет теперь присутствовать во всех выпусках (может, чего-нибудь другое придумаю, чтобы не нагружать выпуски). С левой стороны указана экспертная группа, а справа, в скобках типичный вопрос к данным экспертам, после этого указан адрес с меткой в поле "Тема", откуда нужно будет отправлять вопрос:

виды и типы компьютеров ("У меня 486. Это нормально и чем он отличается от Pentium?"), assembler@beep.ru?Subject=QuestionExpertComputers;

команды DOS (что такое файл, как копировать файлы, как запускать программы и т.п.), assembler@beep.ru?Subject=QuestionExpertDOS;

оболочки под DOS (Norton Commander, DOS Navigator, Volcov Commander и т.п.), assembler@beep.ru?Subject=QuestionExpertDOSShells;

основы работы с Windows'95/98/2000 / NT ("Почему не запускается MASM под Win NT?"), assembler@beep.ru?Subject=QuestionExpertWin;

работа с ассемблерами (MASM/TASM; как сассемблировать программу, что означают другие параметры в командной строке), assembler@beep.ru?Subject=QuestionExpertAssemblers;

работа с отладчиком AFD, assembler@beep.ru?Subject=QuestionExpertAFD;

работа с отладчиком CodeView, assembler@beep.ru?Subject=QuestionExpertCV;

работа с отладчиком TurboDebugger, assembler@beep.ru?Subject=QuestionExpertTD;

работа с дизассемблерами (как и что), assembler@beep.ru?Subject=QuestionExpertDisassm;

Ассемблер: общие вопросы, не рассматриваемые в рассылке ("Посмотрите мою программу и исправьте ошибки"), assembler@beep.ru?Subject=QuestionExpertgeneralassm.

Пожалуйста, проверяйте, чтобы в поле "Тема" Вашего вопроса была соответствующая строка, которая идет после "?Subject=" !!!

Вопросы, которые вы присылали мне и не получили ответ, пожалуйста, перешлите соответствующим экспертам. Спасибо.

Оболочка Super Shell!

Сегодня у нас довольно-таки нудный и неинтересный выпуск. Но то, что мы будем рассматривать в нем, очень важно! Вам придется вспомнить основы математики и немного "пошевелить мозгами". Да-да, друзья мои! А как вы хотели выучить Ассемблер без элементарных знаний математики? Я подчеркиваю: элементарных. Вам придется хорошо поработать сегодня! Хотя, большую часть работы я для вас сделал, а именно: продумал алгоритм вывода на экран любого окна (или рамки) в центр экрана. Вам осталось только внимательно с ним ознакомиться и разобраться.

Полная просьба: внимательно ознакомьтесь с данным выпуском и прилагаемым к нему файлом Sshell14.rar!

Файл-приложение можно взять здесь: http://www.Kalashnikoff.ru/Assembler/Programs/Lessons/Sshell14.rar

Если у вас нет выхода в Сеть, то напишите мне письмо с просьбой выслать. Я также включу ваш адрес в базу данных. Затем, перед выходом очередной рассылки, вы получите этот файл по почте.

Давайте договоримся о некоторых определениях. Т.к. программа у нас будет большая, то для облегчения читаемости и поиска необходимой процедуры, я буду указывать в скобках процедуру и файл, где нужно искать то, о чем я напишу в рассылке. Например,

"...Затем мы удаляем файл (Delete_file, FILES.ASM)..."

Здесь: Delete_file - процедура, которая удаляет файл, а FILES.ASM - файл, где нужно ее искать.

Ну, понеслась...

Сразу заглядываем в файл MAIN.ASM. Перво-наперво, что нам нужно сделать, так это спрятать курсор (Hide_cursor, DISPLAY.ASM). Прежде, чем это сделать, нужно запомнить его текущую позицию. Это позволяет сделать функция 3 прерывания 10h:

Вход: AH=3BH=видеостраница, с которой получить текущую позицию курсора

Выход: DX = текущая позиция курсора (DH - строка, DL - колонка) Как видите, BH должен указывать на видеостраницу, с которой нужно сосчитать позицию курсора. Как уже вам известно, видеокарта имеет достаточно памяти для размещения 8 видеостраниц. Каждая видеостраница имеет свой курсор. Но мы-то будем отображать все на нулевой странице, следовательно, мы должны в BH загрузить 0. На выходе DX будет содержать текущую позицию курсора: DH - строку, а DL - колонку. Причем, отсчет идет с нуля.

После того, как мы получили текущую позицию курсора, можно его прятать. Самый распространенный метод - установка курсора за границу экрана. В нашем случае - установка на 25 строку. Режим-то у нас будет 3 (25 строк, 80 колонок). Т.к. отсчет идет с нуля, то последняя позиция курсора (самая нижняя строка) - 24. Следовательно, установив курсор на 25 строку (19h), мы его просто "прячем"!

Установить курсор на нужную позицию на экране позволяет сделать функция 2 прерывания 10h:

Вход: AH=2BH - видеостраница, на которой будем устанавливать курсор

DH - строка, DL - столбец для установки

Выход: Ничего После того, как мы спрятали курсор, можем переходить к сохранению текущего содержания видеостраницы 0. Как вы знаете, все уважающие себя оболочки сохраняют и, при необходимости, показывают пользователю то, что изображено на экране. Например, Norton Commander или DOS-Navigator, которые отображают пользовательский экран при нажатии на клавиши <Ctrl+O>.

Как ее сохранить? Помните, я говорил о том, что у видеокарты есть достаточно памяти для размещения 8 видеостраниц? Почему бы нам не скопировать содержимое пользовательской, нулевой страницы в первую? А что тут такого?

Вот мы это и делаем (Save_mainscr, DISPLAY.ASM). Здесь мы встречаем новые операторы: PUSHA и POPA (POPA значит POP All - вытолкнуть все (а не то самое...), а PUSHA - PUSH All - втолкнуть все). Т.е. этот оператор толкает в стек регистры в следующем порядке:

AX, CX, DX, BX, SP, BP, SI и DI

и, соответственно, оператор POPA выталкивает их из стека в обратном порядке, т.е.:

DI, SI, BP, SP, BX, DX, CX и AX.

Данные операторы используются только 286+ процессором. Когда пишут, например, что такой-то оператор используется в 386+ процессоре, то это значит, что данный оператор будет работать на процессорах 80386, 80486, Pentium и т.д., но никак не на 8086 (PC/XT) и 80286 (PC/AT).

В нашем случае команды PUSHA и POPA будут работать на 80286 и последующих (более современных) процессорах. Для этого в самом начале файла SSHELL.ASM мы указываем т.н. директиву .286, что говорит ассемблеру (MASM, TASM) о том, что в данной программе могут встречаться команды (инструкции) не только 8086 процессора, но и 80286. "Почему же мы раньше ничего подобного не писали?" - спросит внимательный читатель. "Если ничего подобного не указывать, то ассемблер (MASM, TASM) считает, что в программе используются только инструкции 8086 процессора, что мы до сих пор и делали..." - ответит ваш покорный слуга.

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

Обратите также внимание, как мы заносим в сегментный регистр число:

push 0B800h

pop es

Это получается на 1 байт короче, чем если бы мы делали так:

mov ax,0B800h

mov es,ax

Более того, мы не трогаем регистр AX, что в некоторых случаях бывает полезно.

Команда вида

PUSH 1234h

работает также на процессорах 80286+ (если быть точным, то она работает на процессоре 80186+, но т.к. эти процессоры не получили большого распространения в связи с тем, что через несколько месяцев после выпуска 80186 появились 80286, то о них (о 80186) вообще мало кто знает).

Все остальное в данной процедуре (Save_mainscr, DISPLAY.ASM) должно быть понятно... Точно как и в процедуре восстановления экрана (Restore_mainscr, DISPLAY.ASM)...

_______________

Теперь самое нудное (на мой взгляд).

Курсор спрятали, экран пользовательский сохранили. Теперь можно и окно (рамку) рисовать... Будем это делать не прибегая ни к каким прерываниям, т.к. они работают очень медленно. Воспользуемся выводом символов на экран путем прямого отображения в видеобуфер.

С данным методом мы уже сталкивались, когда пытались выводить на экран "рожицу". Теперь рассмотрим его более подробно.

Метод прямого отображения символов в видеобуфер - вывод символов на экран, путем записи их напрямую в сегмент видеобуфера (в память видеокарты), минуя сервис прерываний.

Стоит отметить, что это самый быстрый способ вывода символов на экран. Быстрее уже не существует. Здесь все зависит от профессионализма программиста и - как следствие - алгоритма вывода, скорости его выполнения.

Рассмотрим вкратце модели мониторов, многие из которых уже уши в прошлое, и только старые и добрые игрушки периодически напоминают об их существовании. Вот ряд: MDA (Monochrome Display Adapter), Hercules, CGA (Color Graphic Adapter), EGA (Enchanced Graphic Adapter), VGA (Video Graphic Array), MCGA (Multi-Color Graphic Adapter - подобие VGA), sVGA (Super Video Graphic Array - то, что многие в настоящий момент используют в работе).

Так вот, при попытке вывода символа в мониторе CGA и подобных ему возникает т.н. "снег". Эта ошибка (недоработка?) была исправлена только в EGA. "Снега" не возникало, если символы выводились при помощи прерываний 10h или 21h. Однако, эти прерывания использовали процедуру, которая проверяла состояние луча монитора, в связи с чем скорость вывода символов существенно замедлялась. Не знаю, есть ли в настоящих ПЗУшках и в прерывании 21h подобная процедура или нет. Главное - при выводе символов через прерывания скорость резко снижается. Полагая, что никто из вас не использует сейчас CGA монитор, будем выводить символы прямым отображением в видеобуфер. Повторю еще раз: это очень быстро даже на 8086 процессорах. Очистка экрана в PC/XT происходит мгновенно. И уж тем более на 80286+...

Для процедуры (а может и не только для нее) заведем две переменные: Height_X и Width_Y. Пока временно. В дальнейшем научимся передавать данные в стеке. Но это будет позже, а сейчас переменная Height_X содержит высоту рамки, а Width_Y - ширину. Num_attr - цвет рамки (Main_proc, MAIN.ASM). Можно вызывать процедуру Draw_frame, которая находится в файле DISPLAY.ASM.

Вот сейчас и рассмотрим самое главное. Итак, процедура Draw_frame.

Перед вызовом данной процедуры необходимо занести некоторые числа в соответствующие переменные (main.asm), а именно:

Height_X - содержит высоту нашей рамки;

Width_Y - содержит ширину нашей рамки;

Num_attr - содержит атрибуты выводимой рамки.

Пока все. Дальше пойдем - больше будет...

Итак, занесли. Вызываем процедуру Draw_frame (Display.asm).

Теперь внимание! Нужно произвести некоторые расчеты перед тем, как вывести рамку не "абы куда", а именно в самый центр экрана.

Нам нужно разделить высоту рамки на 2 и вычесть из полученного числа середину высоты. Вот так:

(1) mov ax,Height_X

(2) shr al,1

(3) mov dh,11

(4) sub dh,al

В строке (1) заносим в AX высоту рамки, которую указали перед вызовом процедуры. Затем, в строке (2), оператор SHR сдвигает все биты в регистре AL на 1 вправо. Что получается? Вот пример:

mov ax,16 --- AX = 10000b, т.е. 16

shr ax,1 --- AX = 1000b, т.е. 8

shr ax,1 --- AX = 100b, т.е. 4

shr ax,1 --- AX = 10b, т.е. 2

shr ax,1 --- AX = 1b, т.е. 1

Т.о. сдвиг битов вправо на единицу делит число на 2. На двойку - в четыре раза и т.д. Это быстрее, если бы мы пользовались оператором DIV (который, кстати, мы еще не изучили) - деление.

В строке (3) мы заносим в DH середину (или почти середину) экрана (по горизонтали), т.е. 11. И вычитаем из 11 полученный результат.

Например, мы выводим рамку высотой в 5 строк. Вот, что получим:

AX=10

AX=AX/2=5

AX=11-5=6

Т.о. вывод начнем со строки 6.

Все тоже самое мы делаем с колонками (вертикально). Посмотрите в прилагаемом примере...

В принципе, пока особо не заостряйте внимание на этом: со временем придет опыт и более точное понимание.

Как только мы вычислили координаты для вывода рамки в центре экрана, мы переводим высоту/ширину в линейный адрес. Что такое линейный адрес и зачем нужно переводить в него?

Как вы уже знаете, то, что находится на экране, расположено по адресу от 0B800:0000 до 0B800:1000h (если нулевая страница). Видеокарта не знает, что такое колонка и строка. Для нее все данные расположены в линейном массиве, т.е. от 0 до 1000h. Т.к. один символ занимает 2 байта (символ и атрибут), то одна строка 3 режима занимает 160 байт (80 строк * 2 = 160). Для того, чтобы вывести символ в первую строку (т.е. во вторую, т.к. отсчет с нуля), нам нужно выводить его по адресу 161, смещения 0B800h. Следовательно, если мы указываем строку/столбец, то нам необходимо перевести эти числа в линейный массив, "понятный" видеокарте. Вот пример (разбираем внимательно!):

mov dh,0 ;DH обычно указывает на строку

mov dl,15 ;а DL - на столбец (это мы и будем делать во всех наших процедурах)

;т.о. мы выводить будем в нулевой строке (т.е. самой верхней на экране), 15 столбце.

Вот переводим два числа в линейный массив (переведем в одно число - смещение):

Linear = DL*2

Умножаем на два строку, т.к. один символ на экране занимает два байта: символ+его атрибут

Linear = Linear + DH*160

Теперь переменная Linear равна 30. Вот он, линейный адрес: 0B800:001Eh (1Eh = 30)!

Еще пример:

mov dh,8 ;строка 8

mov dl,56 ;столбец 56

Linear = DL*2

теперь Linear = 112

Linear = Linear + DH*160

Теперь Linear = 1392 или 570h

И последний пример. Произведем вычисления для последнего символа на экране (нижний правый угол):

Linear = (24 * 160 + 80 * 2) - 2 = 0F9Eh

80 - количество столбцов на экране, которое нужно умножить на 2 (учитывая не только символ, но и его атрибут).

24 - количество строк на экране, начиная от 0. Т.к. в одной строке 80 символов (начиная от 1) * 2 = 160, то, для того, чтобы получить адрес самого последнего (24-ого) ряда, нужно 24*160.

Затем все это дело сложить (адрес 24-ой строки плюс адрес последнего символа минус два). Еще раз:

(24-ая строка * 160 + 80-ый символ * 2) - 2

или

(24 * 160 + 80 * 2) - 2 = 3998 или 0F9Eh

Почему "минус два"? Потому, что... А, хотя, подумайте сами... Я и так тут порасписывал...

Я лучше расскажу, как это выглядит на практике. Итак, процедура (Get_linear, DISPLAY.ASM) (хотя, многое и так понятно из описаний в файле):

shl dl,1 ;умножаем DL на 2 (DL=DL*2)...

Просто сдвинем биты на 1 влево (по принципу деления. См. выше).

mov al,dh ;в AL - ряд,

mov bl,160 ;который нужно умножить на 160

mul bl ;умножаем: AL (ряд) * 160; результат в AX

Что-что? Новый оператор MUL? Ну так рассмотрим сейчас.

mov di,ax ;результат умножения в DI

xor dh,dh ;аннулируем DH

add di,dx ;теперь в DI линейный адрес в видеобуфере...

Итак, вот таблицы с новыми операторами:

 

Название Перевод Применение Процессор shl источник, приемник SHift Left - сдвиг влево Сдвиг байт 8086 Пример:

mov ax,100b

shl ax,1 ; Теперь в AX число 1000b

--------

Название Перевод Применение Процессор shr источник, приемник SHift Right - сдвиг вправо Сдвиг байт 8086 Пример:

mov ax,100b

shr ax,1 ; Теперь в AX число 10b

--------

Название Перевод Применение Процессор mul bl Multiplex - умножение Умножение 8086 Умножает AL на BL, помещая результат в AX. Пример:

mov al,5

mov bl,2

mul bl ; Теперь в AX число 10

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

Если у Вас есть вопросы, то задавайте их, пожалуйста, экспертам (в редких случаях можно и мне).

Удачного Вам программирования!

С уважением,

Автор рассылки:

Калашников Олег

www.Kalashnikoff.ru

E-mail:

assembler@beep.ru

UIN (Тетя Ася):

68951340

(С) Авторское право. Запрещается использование материала из рассылки в коммерческих целях без письменного согласия автора. [Следующий выпуск] [На главную страницу]

u="u496.71.spylog.com";d=document;nv=navigator;na=nv.appName;p=1; bv=Math.round(parseFloat(nv.appVersion)*100); n=(na.substring(0,2)=="Mi")?0:1;rn=Math.random();z="p="+p+"&rn="+rn;y=""; y+=""; y+=""; y+=""; d.write(y);if(!n) { d.write("

Соседние файлы в папке Выпуски