Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование модемов

© Александр Фролов, Григорий Фролов
Том 4, М.: Диалог-МИФИ, 1993, 236 стр.

[Назад] [Содеожание] [Дальше]

2.1. Порты асинхронного адаптера

На этапе инициализации системы, модуль POST BIOS тестирует имеющиеся асинхронные порты RS-232-C и инициализирует их. В зависимости от версии BIOS инициализирует первые два или четыре порта. Их базовые адреса располагаются в области данных BIOS начиная с адреса 0000:0400h.

Первый адаптер COM1 имеет базовый адрес 3F8h и занимает диапазон адресов от 3F8h до 3FFh. Второй адаптер COM2 имеет базовый адрес 2F8h и занимает адреса 2F8h...2FFh. Третий адаптер COM3 имеет базовый адрес 3E8h и занимает диапазон адресов от 3E8h до 3EFh. Четвертый адаптер COM4 имеет базовый адрес 2E8h и занимает адреса 2E8h...2EFh.

Асинхронные адаптеры могут вырабатывать прерывания:

  • COM1, COM3 - IRQ4 (соответствует INT 0Ch);
  • COM2, COM4 - IRQ3 (соответствует INT 0Bh).

Рассмотрим назначение отдельных битов этих портов.

Регистр данных

Регистр данных расположен непосредственно по базовому адресу порта RS-232-C и используется для обмена данными и для задания скорости обмена.

Для передачи данных в этот регистр необходимо записать передаваемый байт данных. После приема данных от внешнего устройства принятый байт можно прочитать из этого же регистра.

В зависимости от состояния старшего бита управляющего регистра (расположенного по адресу base_adr + 3, где base_adr соответствует базовому адресу порта RS-232-C) назначение этого регистра может изменяться. Если старший бит равен нулю, регистр используется для записи передаваемых данных. Если же старший бит равен единице, регистр используется для ввода значения младшего байта делителя частоты тактового генератора. Изменяя содержимое делителя, можно изменять скорость передачи данных. Старший байт делителя записывается в регистр управления прерываниями по адресу base_adr + 1.

Зависимость скорости передачи данных от значения делителя частоты представлена в следующей таблице:

Делитель, десятичная форма Делитель, шестнадцатеричная форма Скорость передачи в бодах
1040 600h 110
768 300h 150
384 180h 300
192 0C0h 600
96 60h 1200
48 30h 2400
24 18h 4800
12 0Ch 9600
6 6h 19200
3 3h 38400
2 2h 57600
1 1h 115200

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

Регистр управления прерываниями

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

В режиме управления прерываниями регистр имеет следующий формат:

 7 6 5 4 3 2 1 0
--T-T-T-T-T-T-T-¬
¦       ¦ ¦ ¦ ¦ ¦
LT+-+-+T+T+T+T+T-
 L==T==- ¦ ¦ ¦ L= 1 - Разрешение прерывания при готовности
    ¦    ¦ ¦ ¦        принимаемых данных
    ¦    ¦ ¦ ¦
    ¦    ¦ ¦ L=== 1 - Разрешение прерывания после передачи
    ¦    ¦ ¦          байта (когда выходной буфер передачи
    ¦    ¦ ¦          пуст)
    ¦    ¦ ¦
    ¦    ¦ L===== 1 - Разрешение прерывания по обнаружению
    ¦    ¦            состояния BREAK или по ошибке
    ¦    ¦
    ¦    L======= 1 - Разрешение прерывания по изменению
    ¦                 состояния входных линий на разъеме
    ¦                 RS-232-C (CTS, DSR, RI, DCD)
    ¦
    L============ Не используются, должны быть равны 0

Для удобства доступа к регистрам UART мы определили для каждого регистра соответствующее объединение (см. файл uart_reg.h).

Далее после описания каждого регистра будет следовать соответствующее ему объединение.

// регистр управления прерываниями

#define ICR_N     1      // смещение относительно базового адреса

typedef union _ICR_ {

   struct {

      unsigned char in_ready : 1;
      unsigned char out_ready : 1;
      unsigned char err : 1;
      unsigned char change : 1;
      unsigned char reserv : 4;

   } bit_reg;

   unsigned char byte;

} ICR;

Регистр идентификации прерывания

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

Формат регистра:

 7 6 5 4 3 2 1 0
--T-T-T-T-T-T-T-¬
¦         ¦   ¦ ¦
LT+-+-+-+T+T+T+T-
 L===T===- L=¦ L= 1  - Нет прерываний, ожидающих
     ¦       ¦         обслуживания
     ¦       ¦
     ¦       L=== 00 - Состояние модема. Устанавливается при
     ¦                 изменении состояния входных линий
     ¦                 CTS, RI, DCD, DSR. Сбрасывается 
     ¦                 после чтения состояния модема из
     ¦                 регистра состояния модема
     ¦
     ¦            01 - Буфер передатчика пуст. Сбрасывается
     ¦                 при записи новых данных в регистр
     ¦                 данных
     ¦
     ¦            10 - Данные приняты и доступны для чтения.
     ¦                 Сбрасывается после чтения данных
     ¦                 из регистра данных
     ¦
     ¦            11 - Прерывание по линии состояния
     ¦                 приемника, возникает при
     ¦                 переполнении приемника, ошибках
     ¦                 четности или формата данных
     ¦                 или при состоянии BREAK.
     ¦                 Сбрасывается после чтения состояния
     ¦                 линии из регистра состояния линии
     ¦
     L=========== Должны быть равны 0

В файле uart_reg.h данный регистр определен следующим образом:

// регистр идентификации прерывания

#define IIDR_N     2      // смещение относительно базового адреса

typedef union _IIDR_ {

   struct  {

      unsigned char no_inter : 1;
      unsigned char inter_id : 2;
      unsigned char reserv : 5;

   } bit_reg;

   unsigned char byte;

} IIDR;

Управляющий регистр

Управляющий регистр доступен по записи и чтению. Этот регистр управляет различными характеристиками UART: скоростью передачи данных, контролем четности, передачей сигнала BREAK, длиной передаваемых слов (символов).

 7 6 5 4 3 2 1 0
--T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦   ¦ ¦   ¦
LT+T+T+T+T+T+T+T-
 ¦ ¦ ¦ L=¦ ¦ L=¦= Данные биты определяют длину передаваемых
 ¦ ¦ ¦   ¦ ¦      слов в битах:
 ¦ ¦ ¦   ¦ ¦        00 - 5 бит;
 ¦ ¦ ¦   ¦ ¦        01 - 6 бит;
 ¦ ¦ ¦   ¦ ¦        10 - 7 бит;
 ¦ ¦ ¦   ¦ ¦        11 - 8 бит
 ¦ ¦ ¦   ¦ ¦
 ¦ ¦ ¦   ¦ L===== Количество стоповых бит:
 ¦ ¦ ¦   ¦          0 - 1 бит;
 ¦ ¦ ¦   ¦          1 - 2 бита
 ¦ ¦ ¦   ¦
 ¦ ¦ ¦   L======= Четность:
 ¦ ¦ ¦              x0 - контроль на четность не
 ¦ ¦ ¦                   выполняется;
 ¦ ¦ ¦              01 - выполняется проверка на нечетность;
 ¦ ¦ ¦              11 - выполняется проверка на четность
 ¦ ¦ ¦
 ¦ ¦ L=========== Фиксация четности. При установке этого
 ¦ ¦              бита бит четности всегда принимает
 ¦ ¦              значение 0 (если биты 3-4 равны 11) или 1
 ¦ ¦              (если биты 3-4 равны 01)
 ¦ ¦
 ¦ L============= Установка перерыва. Вызывает вывод
 ¦                строки нулей в качестве сигнала
 ¦                BREAK для подключенного устройства
 ¦
 L=============== Бит используется для доступа к регистру
                  установки скорости: 
                   1 - регистр данных и регистр управления
                       прерываниями используются
                       для загрузки делителя частоты
                       тактового генератора;
                   0 - регистр данных и регистр управления
                       прерываниями используются как
                       обычно

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

// управляющий регистр

#define LCR_N     3      // смещение относительно базового адреса

typedef union _LCR_ {

   struct {

      unsigned char len : 2;
      unsigned char stop : 1;
      unsigned char parity : 2;
      unsigned char stuck_parity : 1;
      unsigned char en_break_ctl : 1;
      unsigned char dlab : 1;


   } bit_reg;

   unsigned char byte;

} LCR;

Регистр управления модемом

Регистр управления модемом управляет состоянием выходных линий DTR, RTS и линий, специфических для модемов - OUT1 и OUT2, а также запуском диагностики при соединенных вместе входе и выходе асинхронного адаптера.

Формат регистра:

 7 6 5 4 3 2 1 0
--T-T-T-T-T-T-T-¬
¦     ¦ ¦ ¦ ¦ ¦ ¦
LT+-+T+T+T+T+T+T-
 L=T=- ¦ ¦ ¦ ¦ L= Линия DTR. Сигнал подтверждения связи.
   ¦   ¦ ¦ ¦ ¦    Используется модемами для разрешения
   ¦   ¦ ¦ ¦ ¦    передачи данных между компьютером
   ¦   ¦ ¦ ¦ ¦    и микросхемой UART
   ¦   ¦ ¦ ¦ ¦
   ¦   ¦ ¦ ¦ L=== Линия RTS. Сигнал подтверждения связи.
   ¦   ¦ ¦ ¦      Используется модемами для разрешения
   ¦   ¦ ¦ ¦      передачи данных между компьютером
   ¦   ¦ ¦ ¦      и микросхемой UART
   ¦   ¦ ¦ ¦
   ¦   ¦ ¦ L===== Линия OUT1 (запасная). Для некоторых
   ¦   ¦ ¦        модемов при установке этого бита в единицу
   ¦   ¦ ¦        происходит его аппаратный сброс
   ¦   ¦ ¦
   ¦   ¦ L======= Линия OUT2 (запасная). Если бит D3
   ¦   ¦          содержит единицу, то UART может
   ¦   ¦          вырабатывать прерывания, а если нулю -
   ¦   ¦          не может
   ¦   ¦
   ¦   L========= Запуск диагностики при входе
   ¦              асинхронного адаптера, замкнутом
   ¦              на его выход (Digital Loopback test). Эта
   ¦              возможность реализована только для
   ¦              асинхронных портов, использующих
   ¦              микросхему UART 8250, или полностью
   ¦              совместимых с ней
   ¦
   L============= Должны быть равны 0

Регистр управления модемом определен нами в файле uart_reg.h следующим образом:

// регистр управления модемом

#define MCR_N     4      // смещение относительно базового адреса

typedef union  _MCR_ {

   struct {

      unsigned char dtr : 1;
      unsigned char rts : 1;
      unsigned char out1 : 1;
      unsigned char out2 : 1;
      unsigned char diag : 1;
      unsigned char reserv : 3;

   } bit_reg;

   unsigned char byte;

} MCR;

Регистр состояния линии

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

 7 6 5 4 3 2 1 0
--T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
LT+T+T+T+T+T+T+T-
 ¦ ¦ ¦ ¦ ¦ ¦ ¦ L= Данные получены и готовы для чтения,
 ¦ ¦ ¦ ¦ ¦ ¦ ¦    при чтении данных бит сбрасывается
 ¦ ¦ ¦ ¦ ¦ ¦ ¦
 ¦ ¦ ¦ ¦ ¦ ¦ L=== Ошибка переполнения. Был принят новый
 ¦ ¦ ¦ ¦ ¦ ¦      байт данных, а предыдущий еще не был 
 ¦ ¦ ¦ ¦ ¦ ¦      считан программой. В результате предыдущий
 ¦ ¦ ¦ ¦ ¦ ¦      байт потерян
 ¦ ¦ ¦ ¦ ¦ ¦
 ¦ ¦ ¦ ¦ ¦ L===== Ошибка четности, сбрасывается после
 ¦ ¦ ¦ ¦ ¦        чтения состояния линии
 ¦ ¦ ¦ ¦ ¦
 ¦ ¦ ¦ ¦ L======= Ошибка синхронизации. Возникает, например,
 ¦ ¦ ¦ ¦          при отсутствии стоп-битов
 ¦ ¦ ¦ ¦
 ¦ ¦ ¦ L========= Обнаружен запрос на прерывание
 ¦ ¦ ¦            передачи BREAK - длинная строка нулей
 ¦ ¦ ¦
 ¦ ¦ L=========== Регистр хранения передатчика пуст, в него
 ¦ ¦              можно записывать новый байт для передачи
 ¦ ¦
 ¦ L============= Регистр сдвига передатчика пуст. Этот
 ¦                регистр получает данные из регистра
 ¦                хранения и преобразует их в
 ¦                последовательный вид для передачи.
 ¦                Если этот бит равен единице, то UART может
 ¦                принять очередной символ от компьютера
 ¦
 L=============== Таймаут (устройство не связано с
                  компьютером)

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

// регистр состояния линии

#define LSR_N     5         // смещение относительно базового адреса

typedef union _LSR_ {

   struct {

      unsigned char in_ready : 1;
      unsigned char overflow : 1;
      unsigned char parity : 1;
      unsigned char synxr : 1;
      unsigned char break_detect : 1;
      unsigned char out_ready : 1;
      unsigned char shift_ready : 1;
      unsigned char taimout : 1;

   } bit_reg;

   unsigned char byte;

} LSR;

Регистр состояния модема

Регистр состояния модема определяет состояние управляющих сигналов, передаваемых модемом асинхронному порту компьютера.

 7 6 5 4 3 2 1 0
--T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
LT+T+T+T+T+T+T+T-
 ¦ ¦ ¦ ¦ ¦ ¦ ¦ L= Линия CTS изменила состояние
 ¦ ¦ ¦ ¦ ¦ ¦ ¦
 ¦ ¦ ¦ ¦ ¦ ¦ L=== Линия DSR изменила состояние
 ¦ ¦ ¦ ¦ ¦ ¦
 ¦ ¦ ¦ ¦ ¦ L===== Линия RI изменила состояние. Некоторые
 ¦ ¦ ¦ ¦ ¦        коммуникационные программы определяют по
 ¦ ¦ ¦ ¦ ¦        состоянию этого бита наличие звонка на
 ¦ ¦ ¦ ¦ ¦        телефонной линии
 ¦ ¦ ¦ ¦ ¦
 ¦ ¦ ¦ ¦ L======= Если данный бит равен единице, значит
 ¦ ¦ ¦ ¦          линия DCD изменила свое состояние.
 ¦ ¦ ¦ ¦          Некоторые коммуникационные программы
 ¦ ¦ ¦ ¦          определяют по состоянию этого бита,
 ¦ ¦ ¦ ¦          детектировал ли модем несущую частоту на 
 ¦ ¦ ¦ ¦          телефонной линии
 ¦ ¦ ¦ ¦
 ¦ ¦ ¦ L========= Состояние линии CTS. Эта линия
 ¦ ¦ ¦            используется совместно с линией RTS при
 ¦ ¦ ¦            реализации аппаратного управления потоком
 ¦ ¦ ¦            данных
 ¦ ¦ ¦
 ¦ ¦ L=========== Состояние линии DSR. Эта линия
 ¦ ¦              используется совместно с линией DTR при
 ¦ ¦              аппаратной реализации подтверждения связи
 ¦ ¦
 ¦ L============= Состояние линии RI. Единица означает, что
 ¦                модем обнаружил звонок на телефонной
 ¦                линии
 ¦
 L=============== Состояние линии DCD. Единица означает, что
                  модемом получена несущая частота. Заметим,
                  что при выполнении аналогового теста
                  (Analog test) этот бит должен содержать
                  единицу. Если это не так, то возможно, что
                  модем исправен (для внешних модемов), но
                  кабель, соединяющий модем и компьютер, не
                  полностью соответствует стандарту RS-232

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

// регистр состояния модема

#define MSR_N     6         // смещение относительно базового адреса

typedef union _MSR_ {

   struct {

      unsigned char change_cts : 1;
      unsigned char change_dsr : 1;
      unsigned char change_ri : 1;
      unsigned char change_dcd : 1;
      unsigned char cts : 1;
      unsigned char dsr : 1;
      unsigned char ri : 1;
      unsigned char dcd : 1;

   } bit_reg;

   unsigned char byte;

} MSR;

В качестве примера рассмотрим программу, которая считывает и отображает на дисплее состояние некоторых регистров асинхронного адаптера. Сначала программа запрашивает номер COM-порта, состояние регистров которого вы хотите узнать. Затем она получает базовый адрес регистров этого асинхронного адаптера. Базовый адрес регистров данного асинхронного адаптера программа получает через область переменных BIOS. Вычисление базового адреса выполняется функцией com_address():

#include "sysp_com.h"

/**
*.Name         com_address
*
*.Title        Определяет адрес заданного COM-порта.
*
*.Descr        Эта функция определяет адрес базового регистра
*              COM-порта. Адрес берется из области переменных
*              BIOS.
*
*.Proto        unsigned  com_address( int port );
*
*.Params       int port - номер асинхронного адаптера:
*              0 - COM1, 1 - COM2, 2 - COM3, 3 - COM4.
*
*.Return       Адрес базового регистра асинхронного порта.
*              Если порт не установлен, возвращается 0,
*              если неправильно задан параметр, то -1.
*
*.Sample       com_adr.c
**/

unsigned  com_address( int port ) {

   unsigned base_address;

   // возвращаем -1, если заданный асинхронный порт
   // не COM1, не COM2, не COM3 и не COM4

   if(( port > 4 ) || ( port < 0 )) return( -1 );

   // считываем из области переменных BIOS базовый адрес данного порта

   base_address = *(( unsigned _far * ) FP_MAKE( 0x40, port * 2 ));

   return( base_address );
}

Старые версии BIOS могут самостоятельно определить наличие и адреса только первых двух COM-портов. Таким образом, если на вашем компьютере установлен асинхронный последовательный порт COM3 или COM4, то без дополнительной инициализации данная программа может просто "не увидеть" эти порты.

После определения базового адреса COM-порта, программа считывает состояние регистров данного порта и отображает их на дисплее.

Итак, приведем основной модуль программы:

// REGISTER.C

#include   "uart_reg.h"
#include   "sysp_com.h"
#include   <graph.h>


unsigned com_address( int );

void main(void) {

   unsigned adr;
   int port;
   unsigned char data;

   LCR     reg_lcr;
   LSR     reg_lsr;
   MCR     reg_mcr;
   MSR     reg_msr;
   ICR     reg_icr;
   IIDR    reg_iidr;

   printf(" Программа отображает состояние регистров UART.\n\n");

   printf(" Введите номер асинхронного порта (1-4): ");
   scanf("%d", &port );

   // определяем базовый адрес адаптера port

   adr = com_address( --port );

   // считываем значение управляющего регистра

   printf("\n Управляющий регистр = %Xh\n\n", data =
        (unsigned char ) inp( adr + LCR_N ) );

   reg_lcr.byte = data;

   printf("    Длина слова в байтах %d\n"
        "    Количество стоповых битов %d\n"
        "    Контроль %s\n"
        "    Фиксация четности %s\n"
        "    Установка перерыва: %s\n"
        "    Порты %Xh, %Xh используются %s\n",

         reg_lcr.bit_reg.len + 5,
         reg_lcr.bit_reg.stop + 1,
         (reg_lcr.bit_reg.parity == 3) ? "на четность" :
            ((reg_lcr.bit_reg.parity == 1 ) ? "на нечетность" :
                  "четности не выполняется"),

         (reg_lcr.bit_reg.stuck_parity == 1) ? "производится" :
                  "не производится",

         (reg_lcr.bit_reg.en_break_ctl == 1) ? "есть" :
                  "нет",

         adr, adr + 1,
         (reg_lcr.bit_reg.dlab == 1) ? "для загрузки делителя частоты" :
                  "как обычно"

   );


   // считываем значение регистра состояния линии 

   printf("\n Регистр состояния линии = %Xh\n\n", data =
        (unsigned char ) inp( adr + LSR_N ) );

   reg_lsr.byte = data;

   printf("    Данные готовы для чтения: %d\n"
        "    Ошибка переполнения: %d\n"
        "    Ошибка четности: %d\n"
        "    Ошибка синхронизации: %d\n"
        "    Обнаружен запрос на прерывание \"BREAK\": %d\n"
        "    Регистр хранения передатчика пуст: %d\n"
        "    Регистр сдвига передатчика пуст: %d\n"
        "    Тайм-аут: %d\n",

         reg_lsr.bit_reg.in_ready,
         reg_lsr.bit_reg.overflow,
         reg_lsr.bit_reg.parity,
         reg_lsr.bit_reg.synxr,
         reg_lsr.bit_reg.break_detect,
         reg_lsr.bit_reg.out_ready,
         reg_lsr.bit_reg.shift_ready,
         reg_lsr.bit_reg.taimout
   );

   printf("\n\n Нажмите любую клавишу для продолжения ");
   getch();


   // считываем значение регистра управления модемом

   printf("\n Регистр управления модемом = %Xh\n\n", data =
        ( unsigned char ) inp( adr + MCR_N ));
   reg_mcr.byte = data;


   printf("    Линия dtr: %d\n"
        "    Линия rts: %d\n"
        "    Линия out1: %d\n"
        "    Линия out2: %d\n"
        "    Запуск диагностики: %d\n",

         reg_mcr.bit_reg.dtr,
         reg_mcr.bit_reg.rts,
         reg_mcr.bit_reg.out1,
         reg_mcr.bit_reg.out2,
         reg_mcr.bit_reg.diag
   );

   // считываем значение регистра состояния модема 

   printf("\n Регистр состояния модема = %Xh\n\n", data =
        ( unsigned char ) inp( adr + MSR_N ) );
   reg_msr.byte = data;


   printf("    Линия изменила cts состояние: %d\n"
        "    Линия изменила dsr состояние: %d\n"
        "    Линия изменила ri состояние: %d\n"
        "    Линия изменила dcd состояние: %d\n"
        "    Линия cts: %d\n"
        "    Линия dsr: %d\n"
        "    Линия ri: %d\n"
        "    Линия dcd: %d\n",

         reg_msr.bit_reg.change_cts,
         reg_msr.bit_reg.change_dsr,
         reg_msr.bit_reg.change_ri,
         reg_msr.bit_reg.change_dcd,
         reg_msr.bit_reg.cts,
         reg_msr.bit_reg.dsr,
         reg_msr.bit_reg.ri,
         reg_msr.bit_reg.dcd
   );


   printf("\n\n Нажмите любую клавишу для продолжения ");
   getch();

   // считываем значение регистра управления прерываниями 

   printf("\n\n Регистр управления прерываниями = %Xh\n\n", data =
        ( unsigned char ) inp( adr + ICR_N ));

   reg_icr.byte = data;


   printf("    Разрешение прерывания при готовности принимаемых данных: %d\n"
        "    Разрешение прерываний после передачи данных: %d\n"

        "    Разрешение прерывания при обнаружении"
        "    состояния \"BREAK\": %d\n"

        "    Разрешение прерыывания при изменении состояния линий"
        " cts, dsr, ri, dcd: %d\n",

         reg_icr.bit_reg.in_ready,
         reg_icr.bit_reg.out_ready,
         reg_icr.bit_reg.err,
         reg_icr.bit_reg.change
   );


   // считываем значение регистра идентификации прерываний

   printf("\n\n Регистр идентификации прерываний = %Xh\n\n", data =
        ( unsigned char ) inp( adr + IIDR_N ));

   reg_iidr.byte = data;


   printf("    Нет прерываний, ожидающих обслуживания: %d\n"
        "    Идентификатор прерывания: %d\n",

         reg_iidr.bit_reg.no_inter,
         reg_iidr.bit_reg.inter_id
   );
}

[Назад] [Содеожание] [Дальше]