Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Биты, байты, слова, регистры, осн. команды. Для тех кто хочет изучить ASM с азов ч.1 
:(
    Опции темы
froggy82
Дата 18.10.2006, 08:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 52
Регистрация: 25.9.2006
Где: Архангельск

Репутация: нет
Всего: нет



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

Биты, байты, слова


Минимальной единицей информации в компьютере является бит. Бит может принимать два состояния – ноль или единица. Если двоичное число состоит из n разрядов, то с его помощью можно закодировать ровно 2n различных значений. Минимальное из этих чисел – ноль (все биты двоичного числа равны нулю), максимальное = 2n–1 (все биты двоичного числа равны единице).
Группа из восьми битов называется байтом. Один байт может принимать 28 (256) различных значений: от "все выключены" (000000002 = 010) до "все включены" (111111112 = 25510). 
По соглашению биты в байте пронумерованы от 0 до 7 справа налево (от младших разрядов к старшим). Два байта образуют слово. Биты в слове пронумерованы от 0 до 15 от младших разрядов к старшим.

Регистры


Данные, с которыми работает процессор, должны находиться в регистрах. Регистры – это ячейки памяти, которые находятся в процессоре. Как и в любые ячейки памяти в регистры можно записывать и считывать различные числа. Максимальное число, которое можно записать в регистр зависит от разрядности регистра (количества двоичных разрядов). До появления процессора Intel 80386 разрядность регистров была 16 бит (одно слово). Современные модели процессоров работают с 32-х битными регистрами (все регистры общего назначения, 80386 и выше), 64-х битными (FPU, MMX, Pentium и выше) и 128-и битными (SSE, Pentium II и выше).
Регистры могут быть классифицированы следующим образом:
1. Регистры общего назначения. Это восемь 32-х битных регистров, которые могут произвольно использоваться программистом.
Регистрами общего назначения являются 32-х битные регистры EAX, EBX, ECX, EDX, EBP, ESP, ESI и EDI. Данные регистры используются для хранения операндов логических и арифметических команд. 
В каждом регистре общего назначения выделяют младшую часть, как самостоятельный регистр (их тоже 8 – AX, BX, CX, DX, BP, SP, SI и DI). Собственно, до процессора Intel 80386 только они и существовали.
У каждого байта 16-и битных регистров AX, BX, CX и DX есть свое обозначение: старшие байты называются AH, BH, CH и DH (старшие байты) а младшие – AL, BL, CL и DL. На схеме рассмотрен регистр EAX:
Все регистры общего назначения могут использоваться для адресных вычислений и для получения результатов большинства арифметических и логических операций. Тем не менее, некоторые команды используют фиксированные регистры для хранения операндов. Например, команды обработки строк используют в качестве операндов содержимое регистров ECX, ESI и EDI. Использование фиксированных регистров для некоторых операций позволяет более компактно кодировать набор команд. Следующие команды используют фиксированные регистры: умножение и деление, ввод/вывод, обработка строк, перекодирование, цикл, сдвиговые операции, операции со стеком.
2. Сегментные регистры. Данные регистры содержат номера сегментов, соответствующих различным адресам памяти. Например, существуют специальные сегментные регистры для доступа к пространству кода, области данных и стека. В защищенном режиме для пользовательских программ сегментные регистры изменять нет необходимости.
3. Регистр флагов – определяет и позволяет изменять состояние процессора. Каждый его бит называется «флагом» и может быть установлен или сброшен в зависимости от результата выполнения очередной команды. Также некоторые команды могут выполнять какие-то действия только в том случае, если установлен определенный флаг (см. например Jxx).

Основные команды


Пересылка данных

MOV    приемник, источник

Базовая команда пересылки данных. Копирует содержимое источника в приемник, источник не изменяется. Команда MOV действует аналогично операторам присваивания из языков высокого уровня, то есть команда
mov    ax, bx
эквивалента выражению
ax := bx;
языка Pascal или
ax = bx;
языка C, за исключением того, что команда ассемблера позволяет работать не только с переменными в памяти, но и со всеми регистрами процессора.
В качестве источника для MOV могут использоваться: число (непосредственный операнд), регистр общего назначения, сегментный регистр или переменная (то есть операнд, находящийся в памяти); в качестве приемника: регистр общего назначения, сегментный регистр (кроме CS) или переменная. Оба операнда должны быть одного и того же размера – байт, слово или двойное слово.

Модификаторы чисел

Для того чтобы записать в регистр числа других систем счисления используются модификаторы – буквы, приписываемые к числу справа. Если необходимо записать двоичное число следует дописать b, если шестнадцатеричное – h. Для десятичных чисел модификатор не требуется. Например:

mov    al, 11b {записывает в AL двоичное число 000000112 = 310}
mov    al, 11    {записывает в AL десятичное число 11}
mov    al, 11h {записывает в AL шестнадцатеричное число 1116 = 1710}

Примечание: если шестнадцатеричное число начинается с буквы, то перед ним необходимо записать ноль:

mov al,0f1h {записывает в AL шестнадцатеричное число f1}

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

Арифметические операции


Сложение

ADD    приемник, источник

Команда выполняет арифметическое сложение приемника и источника, помещает сумму в приемник, не изменяя содержимое источника. Приемник может быть регистром или переменной, источник – числом, регистром или переменной, но нельзя использовать переменную одновременно и для источника, и для приемника. Команда ADD никак не различает числа со знаком и без знака, но, употребляя значения флагов CF (перенос при сложении чисел без знака), OF (перенос при сложении чисел со знаком) и SF (знак результата), разрешается применять ее и для тех, и для других.

Вычитание

SUB    приемник, источник

Вычитает источник из приемника и помещает разность в приемник. Приемник может быть регистром или переменной, источник – числом, регистром или переменной, но нельзя использовать переменную одновременно и для источника и для приемника. Точно так же, как и команда ADD, SUB не делает различий между числами со знаком и без знака, но флаги позволяют использовать ее и для тех, и для других.

Умножение чисел без знака

MUL    источник

    Выполняет умножение содержимого источника (регистр или переменная) и регистра AL, AX, EAX (в зависимости от размера источника) и помещает результат в AX, DX:AX, EDX:EAX соответственно. Если старшая половина результата (AH, DX, EDX) содержит только нули (результат целиком поместился в младшую половину), флаги CF и OF устанавливаются в 0, иначе – в 1. Значение остальных флагов (SF, ZF, AF и PF) не определено. Некоторые варианты использования команды MUL приведены в таблице:

Команда    Действие    Результат    Команда    Действие    Результат

mul al          al * al               ax            mul eax        eax * eax    edx:eax
mul ah         al * ah              ax            mul ebx       eax * ebx    edx:eax
mul dl          al * dl               ax            mul ecx         eax * ecx    edx:eax
mul dh         al * dh              ax            mul edx       eax * edx    edx:eax
mul ax         ax * ax            dx:ax         mul esi         eax * esi    edx:eax
mul bx         ax * bx            dx:ax         mul edi         eax * edi    edx:eax
mul cx         ax * cx             dx:ax         mul ebp       eax * ebp    edx:eax
mul dx         ax * dx            dx:ax         mul esp       eax * esp    edx:eax

Целочисленное деление без знака

DIV    источник

    Выполняет целочисленное деление без знака AX, DX:AX, EDX:EAX (в зависимости от размера источника) на источник (регистр или переменная) и помещает результат в AL, AX или EAX, а остаток – в AH, DX или EDX соответственно. Результат всегда округляется в сторону нуля, абсолютное значение остатка меньше абсолютного значения делителя. Флаги CF, OF, SF, ZF, AF и PF после этой команды не определены, а переполнение или деление на ноль вызывает исключение #DE (ошибка при делении) в защищенном режиме и прерывание 0 – в реальном. Несколько примеров использования команды DIV приведены в таблице:

Команда    Действие    Целое    Остаток    Команда    Действие    Целое    Остаток
div al            ax/al            al               ah         div eax       edx:eax/eax    eax    edx
div ah          ax/ah           al                ah         div ebx       edx:eax/ebx    eax    edx
div dl            ax/dl           al                 ah         div ecx       edx:eax/ecx    eax    edx
div dh          ax/dh           al                 ah         div edx    edx:eax/edx      eax    edx
div ax        dx:ax/ax        ax                dx         div esi       edx:eax/esi      eax    edx
div bx        dx:ax/bx        ax                dx         div edi       edx:eax/edi     eax    edx
div cx        dx:ax/cx        ax                dx         div ebp       edx:eax/ebp    eax    edx
div dx        dx:ax/dx        ax                dx         div esp       edx:eax/esp    eax    edx

Изменение знака

NEG приемник

Выполняет над числом, содержащимся в приемнике (регистр или переменная), операцию дополнения до двух. Эта операция эквивалентна обращению знака операнда, если рассматривать его как число со знаком. Если приемник равен нулю, флаг CF устанавливается в 0, иначе – в 1. Остальные флаги (OF, SF, ZF, AF, PF) назначаются в соответствии с результатом операции.

Инкремент/декремент

INC приемник                ;Инкремент

Увеличивает приемник (регистр или переменная) на 1. Единственное отличие этой команды от ADD приемник,1 состоит в том, что флаг CF не затрагивается. Остальные арифметические флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом сложения.

DEC приемник                ;Декремент

Уменьшает приемник (регистр или переменная) на 1. Единственное отличие этой команды от SUB приемник,1 состоит в том, что флаг CF не затрагивается. Остальные арифметические флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом вычитания.

Сравнение


CMP    приемник, источник

    Сравнивает приемник и источник и устанавливает флаги. Действие осуществляется путем вычитания источника (число, регистр или переменная) из приемника (регистр или переменная; приемник и источник не могут быть переменными одновременно), причем результат вычисления никуда не записывается. Единственным следствием работы этой команды оказывается изменение флагов CF, OF, SF, ZF, AF, PF. Обычно команду CMP используют вместе с командами условного перехода (Jxx), которые позволяют применить результат сравнения, не обращая внимания на детальное значение каждого флага.

Команды передачи управления


Безусловный переход

JMP    операнд

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

@label:        ; описание метки
...            ; код программы 
jmp @label    ; переход на метку

Условный переход

Jxx    метка

    Это набор команд, выполняющих переход, если удовлетворяется соответствующее условие, которым в каждом случае реально является состояние тех или иных флагов. Здесь J означает jump, а xx – условие, при котором необходимо совершить переход на метку. Когда команда из набора Jxx используется сразу после команды CMP, условия приобретают формулировки, соответствующие отношениям между операндами CMP (см. таблицу).

Код команды     Реальное условие     Условие для CMP
    JE                                ZF = 1               Если равно
    JZ                                ZF = 1               Если ноль                 

    JNE                              ZF = 0               Если не равно
    JNZ                              ZF = 0               Если не ноль

    JA                                CF = 0               Если выше
    JNBE                            CF = 0               Если не ниже и не равно

    JAE                      CF = 0 или ZF = 0     Если выше или равно
    JNB                      CF = 0 или ZF = 0      Если не ниже

    JB                                CF = 1               Если ниже
    JNAE                            CF = 1               Если не выше и не равно

    JBE                      CF = 1 или ZF = 0     Если ниже или равно
    JNA                      CF = 1 или ZF = 0     Если не выше
Примечание: слова «выше» и «ниже» в таблице относятся к сравнению чисел без знака.

Цикл

LOOP    метка

    Уменьшает регистр ECX на 1 и выполняет переход на метку, если ECX не равен нулю. Эта команда используется для организации циклов, в которых регистр ECX играет роль счетчика. Так, в следующем примере команда ADD выполнится 10 раз:

    xor    eax, eax        ;обнуляем регистр EAX 
    mov    ecx, 0Ah        ;перед началом цикла в ECX 
                                     ;помещаем количество итераций
@loop_start:
    add    eax, ecx
    loop    @loop_start

ПРОДОЛЖЕНИЕ СЛЕДУЕТ...



PM MAIL ICQ   Вверх
koljan
Дата 18.10.2006, 14:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


VIP
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 18.12.2005
Где: г.Архангельск

Репутация: нет
Всего: 1



АГА а вот и продолжение


Работа с оперативной памятью 

Запись и чтение памяти осуществляется также с помощью команды mov. Если известен адрес ячейки памяти до компиляции программы, то можно подставить его в виде константы:

mov    eax, [40128h]

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

mov    ebx, 40128h
mov    eax, [ebx]

Примечание: Не рекомендуется записывать и считывать ячейки памяти по произвольным адресам. Обычно это рано или поздно приводит к появлению исключений.

Сегменты и смещения

Процессор могут работать в двух режимах: реальный и защищенный. Реальный режим эмулирует особенности (и недостатки) процессора Intel 8086. Одним из таких недостатков является использование 16-и разрядной адресации, и, как следствие, максимальный размер блока памяти 216 байт (64 Кб). Но даже у первых процессоров памяти было гораздо больше. Чтобы решить эту проблему, память компьютера условно поделена на так называемые сегменты. Сегментом называется область памяти, которая начинается на границе параграфа, то есть с любого адреса, кратного 16 (четыре младших бита равны нулю).
Таким образом, если мы знаем значение сегмента, то легко подсчитать физический адрес в памяти, умножив это значение на 16: если сегмент = 64h, то физический адрес = 64h • 10h = 640h. То есть, умножение шестнадцатеричного числа на 16 эквивалентно приписыванию нуля справа от исходного числа, например:

32h • 10h = 320h
128h • 10h = 1280h

Теперь нужно задать смещение (offset) – количество байт от начала заданного сегмента до требуемой ячейки. Итак, абсолютный адрес в памяти вычисляется как сумма номера сегмента, умноженного на 16, и смещения. Сегменты и смещения записываются в командах ассемблера следующим образом:

сегмент:[смещение]

где
•    сегмент – это один из сегментных регистров (CS, DS, ES, FS, GS или SS), в который предварительно занесён номер сегмента;
•    смещение – это непосредственное значение смещения или регистр общего назначения, в котором хранится значение смещения.

Например:

mov    ax, 0b800h
{записываем номер сегмента в сегментный регистр ES}
mov    es, ax
{записываем значение из ячейки памяти из сегмента 0b800h со смещением 80 в регистр АХ}
mov    ax, es:[80]

mov    bx, 160
{записываем значение из ячейки памяти из сегмента b800h со смещением 160 в регистр АХ}
mov    ax, es:[bx]

Если сегмент не указан явно, например, 

mov    ax, [bx]

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

mov    ax, ds:[bx]

В команде mov допустимы следующие комбинации операндов:

Операнд 1 (приёмник)    Операнд 2 (источник)
РОН    РОН, адрес, число, сегментный регистр
адрес    РОН, число, сегментный регистр
сегментный регистр (кроме CS)    РОН, адрес

 

Как видно из таблицы, в процессорах Intel реализованы не все комбинации копирования, например нельзя скопировать в сегментный регистр число. Но в таких случаях можно воспользоваться двумя командами mov и одним из регистров общего назначения:

{Ошибка}
mov es, 200
{Правильно}
mov ax, 200
mov es, ax

Защищенный режим

    В защищенном режиме также присутствуют и сегменты, и сегментные регистры. Но в отличие от реального режима, определить по сегменту его адрес гораздо сложнее. Значение сегментного регистра это номер в специальной таблице дескрипторов, которые, как следует из названия, описывают расположение сегмента в памяти, его размер, параметры доступа (чтение, запись, запись/чтение) и некоторая системная информация. В пользовательском режиме узнать эту информацию нельзя, а значит нельзя определить и реальный адрес сегмента в памяти. На самом деле эта информация для прикладных программ не является необходимой, и распределение сегментов принимают как некоторые исходные данные. Обычно программа связана как минимум с тремя сегментами: кода, данных и стека. В защищенном режиме они совпадают, поэтому использовать префиксы (cs:, ds:, ss:) необязательно. Таким образом, вместо физического адреса реально используется логический адрес, в данном случае это смещение ячейки памяти внутри сегмента.

Работа с переменными

На любом языке программирования при написании программ невозможно обойтись без использования переменных – в терминах языка ассемблера это просто ячейки памяти, обращение к которым происходит не по их адресу, а по символьному имени. В общем случае и переменные языков высокого уровня являются такими же ячейками памяти, но обычно реализация работы с ними скрыта от программиста.
В ассемблере для определения переменных используют псевдокоманды db, dw, dd (псевдокомандами называют некоторые специфичные для ассемблера сокращения, которые не транслируются напрямую в машинный код, а на этапе компиляции преобразуются в некоторые числовые значения). 
 

Предположим, мы хотим определить несколько переменных: в одной из них будет храниться число от 0 до 255 (размерность – байт), в другой строка-константа (‘Hello, World!’) и первоначально пустой массив размерностью 32 слова. В таком случае текст программы будет выглядеть следующим образом:

uses Windows;

begin asm
jmp    @start
@data_:
@param:        db    0eah
@hello:        db    ‘Hello, World!’, 0
@array:        dw    0, 0, 0, 0, 0, 0, 0, 0
            dw    0, 0, 0, 0, 0, 0, 0, 0
            dw    0, 0, 0, 0, 0, 0, 0, 0
            dw    0, 0, 0, 0, 0, 0, 0, 0

@start:
{Необходимая инициализация.}
{Обязательно наличие в программе меток @data_ до начала блока данных и @start после.}

lea     eax, [esp - 4]
push    eax
push    $00000040
push    offset @start
sub     dword ptr [esp], offset @data_
push    offset @data_
call    VirtualProtect

    {Здесь начинается основная программа.}
[...]
end end.

Примечание: все локальные метки и переменные в программах на встроенном ассемблере Virtual Pascal начинаются с символа @.

Обратите особенное внимание на команду jmp @start. Казалось бы, очевидно, что следующие за ней несколько строк – данные, но ни компилятор, ни процессор об этом не знают. Это очевидно только программисту, который написал подобный код. Поэтому необходима «подсказка», которая заставит процессор обойти нежелательный участок – это и есть команда jmp, команда безусловного перехода. Впрочем, вы можете временно удалить данную строку и попытаться выполнить полученную «программу» – обычно ни к чему хорошему такой «стиль» программирования не приводит – рано или поздно начнут появляться непонятные и трудноуловимые ошибки. В приведенном примере программа после запуска вызывает системное исключение.
Рассмотрим, как компилятор обрабатывает подобные строки. Встретив символ db, dw или dd компилятор «догадывается», что это какие-то специфичные данные (или команды), которые по какой то причине пришлось вводить именно таким образом. Поэтому он резервирует одну ячейку памяти для переменной @param, 14 для переменной @hello (по одному байту на каждый символ, включая запятую и пробел, но без учета кавычек, которые нужны исключительно для выделения границ строки, плюс завершающий ноль, необходимый для определения конца строки) и 64 для переменной @array (в слове два байта, поэтому памяти потребуется в два раза больше чем число элементов массива).

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

    mov    ebx, offset @param

Запомните, что никакой машинной команды offset не существует, это еще одна псевдокоманды. Она сообщает компилятору, что в реальной программе сюда следует подставить адрес, по которому находится переменная @param. Поэтому компилятор преобразует команду в следующую:

    mov    ebx, 401019h

Разумеется, число может оказаться абсолютно любым и зависит в общем случае только от компилятора и расположения переменной в программе. Так как в нашем примере переменные располагаются неподалеку от начала программы, и адрес будет чуть больше 401000h (адрес начала программы).
Поставьте точку останова на команду mov bx, offset @param (Ctrl+F8, отключаются точки останова аналогично), выполните программу до точки останова (Ctrl+F9), выполните исследуемую команду (F7) и посмотрите в окне отладчика (меню View, пункт CPU) чему равен адрес переменной в вашей программе. Очевидно, что если известно, куда попадет наша переменная после компиляции, можно написать это число вручную. До появления ассемблера, кстати, программисты так и делали.

Теперь можно считать переменную из памяти в какой нибудь регистр общего назначения. Так как размер переменной составляет один байт, регистры AX, BX, CX или DX не подходят – они слишком большие. Использовать нужно старшую или младшую часть любого из регистров. Например, AL:

    mov    al, [ebx]

Снова ставим точку останова на команду mov al, [bx], выполняем программу до точки останова (Ctrl F9) и вызываем окно CPU. Затем нажимаем F7 (пошаговое выполнение) и смотрим, что будет записано в младшую часть регистра AX. Если все сделано правильно, в младшей части регистра AX находится число 0EAh в шестнадцатеричной системе, как этого и следовало ожидать. В старшей же части может находиться все что угодно.

Аналогично осуществляется и запись в переменную:

    mov    [ebx], al

Но константу, например число 7, таким способом в переменную записать не удастся. Команду

    mov    [ebx], 7

компилятор откажется распознавать. Все дело в том, что неизвестно, сколько байтов должно занимать число 7 в памяти. Ведь это может быть переменная размером байт, слово или двойное слово. Поэтому размер переменной придется указывать явно в виде byte ptr, word ptr или dword ptr для байта, слова или двойного слова соответственно.

Для нашего примера это будет следующая команда:

    mov    byte ptr [ebx], 7

Кстати, в такой форме можно уже непосредственно указывать имя переменной:

    mov    byte ptr @param, 7

И, наконец, рассмотрим работу со строками:

    mov    al, byte ptr @hello

считает первую букву строки, то есть ‘H’ в регистр AL;

в то время как команда

    mov    al, byte ptr @hello[7]

загрузит в этот же регистр восьмой символ в строке (нумерация начинается с нуля), то есть ‘W’.

И еще, запомните что при работе со встроенным ассемблером также можно использовать стандартные объявления переменных языка Паскаль, например:

var
    a: array [0..63] of byte;
    i: word;

Работа с ними ничем принципиально не отличается от переменных, объявленных с помощью псевдокоманд db, dw или dd, рассмотренных выше (более того, они располагаются в сегменте данных, что упрощает их использование), но программа в таком случае становится плохо приспособленной к переносу на «чистый» язык ассемблера.



P.S. стянул с сервера smile 


--------------------
PM MAIL ICQ   Вверх
froggy82
Дата 18.10.2006, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 52
Регистрация: 25.9.2006
Где: Архангельск

Репутация: нет
Всего: нет



koljan,  да хороший сервер у нас в АГТУ  smile  Пусть и другие почитают
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Asm: Общие вопросы"
MAKCim
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • Не забывайте пользоваться кнопкой КОД.
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к разделу форума. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, MAKCim.

 
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Asm: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1442 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.